当前位置: 首页 > article >正文

C++学习:类和对象(二)

一、默认成员函数

1. 什么是默认成员函数?

在C++中,每个类都有一些特殊的成员函数,如果程序员没有显式地声明,编译器会自动为类生成这些函数,这些函数称为默认成员函数

2. 默认成员函数列表

  • 默认构造函数(Default Constructor)
  • 默认析构函数(Destructor)
  • 默认拷贝构造函数(Copy Constructor)
  • 默认拷贝赋值运算符(Copy Assignment Operator)
  • 默认移动构造函数(Move Constructor,C++11引入)
  • 默认移动赋值运算符(Move Assignment Operator,C++11引入)

3. 编译器何时生成默认成员函数?

  • 显式声明:如果程序员没有提供某个默认成员函数的定义,编译器会根据需要自动生成
  • 特别注意:一旦程序员显式地声明了任何一个拷贝或移动操作,编译器将不会为该类生成移动操作,需要手动提供

4. 代码示例

#include <iostream>
using namespace std;

class Example {
public:
    int value;

    // 未显式声明任何默认成员函数
};

int main() {
    Example ex1;       // 调用默认构造函数
    ex1.value = 10;

    Example ex2 = ex1; // 调用默认拷贝构造函数

    Example ex3;
    ex3 = ex1;         // 调用默认拷贝赋值运算符

    cout << "ex1.value = " << ex1.value << endl;
    cout << "ex2.value = " << ex2.value << endl;
    cout << "ex3.value = " << ex3.value << endl;

    return 0;
}

二、构造函数

1. 什么是构造函数?

构造函数(Constructor)是在创建对象时自动调用的特殊成员函数,用于初始化对象的成员变量。构造函数的名称与类名相同

2. 特点

  • 没有返回类型(连void也没有)
  • 可以有参数(参数化构造函数)
  • 支持函数重载,即可以有多个构造函数
  • 可以在构造函数初始化列表中初始化成员变量

3. 默认构造函数

如果程序员未提供任何构造函数,编译器会为类生成一个默认构造函数,它对基本类型成员变量不进行初始化

4. 代码示例:

1.默认构造函数

#include <iostream>
using namespace std;

class Person {
public:
    string name;
    int age;

    // 默认构造函数
    Person() {
        name = "Unknown";
        age = 0;
    }

    void display() const {
        cout << "姓名:" << name << ", 年龄:" << age << endl;
    }
};

int main() {
    Person p; // 调用默认构造函数
    p.display();
    return 0;
}

2.参数化构造函数

#include <iostream>
using namespace std;

class Person {
public:
    string name;
    int age;

    // 参数化构造函数
    Person(const string& n, int a) {
        name = n;
        age = a;
    }

    void display() const {
        cout << "姓名:" << name << ", 年龄:" << age << endl;
    }
};

int main() {
    Person p("Alice", 25); // 调用参数化构造函数
    p.display();
    return 0;
}

 3.构造函数初始化列表

#include <iostream>
using namespace std;

class Point {
private:
    int x;
    int y;

public:
    // 使用初始化列表初始化成员变量
    Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}

    void display() const {
        cout << "坐标:(" << x << ", " << y << ")" << endl;
    }
};

int main() {
    Point pt(3, 4);
    pt.display();
    return 0;
}

5. 注意事项

  • 如果类中有const成员变量或引用类型成员,必须使用初始化列表进行初始化
  • 构造函数可以被重载,允许创建多个具有不同参数列表的构造函数

三、析构函数

1. 什么是析构函数?

析构函数(Destructor)是在对象生命周期结束时自动调用的特殊成员函数,用于释放对象占用的资源(如内存、文件等)。析构函数的名称是在类名前加上~符号

2. 特点

  • 没有参数
  • 没有返回类型
  • 每个类只有一个析构函数,不能重载
  • 编译器会自动调用析构函数,无需手动调用

3. 代码示例

#include <iostream>
using namespace std;

class Resource {
public:
    Resource() {
        cout << "资源已分配。" << endl;
    }

    ~Resource() {
        cout << "资源已释放。" << endl;
    }
};

int main() {
    cout << "进入main函数。" << endl;
    {
        Resource res; // 创建对象,调用构造函数
    } // 离开作用域,调用析构函数
    cout << "退出main函数。" << endl;
    return 0;
}

4. 在析构函数中释放资源

当类中使用了动态内存分配(如使用new关键字),需要在析构函数中释放内存,防止内存泄漏

#include <iostream>
using namespace std;

class Array {
private:
    int* data;
    int size;

public:
    Array(int s) : size(s) {
        data = new int[size];
        cout << "数组已分配。" << endl;
    }

    ~Array() {
        delete[] data;
        cout << "数组已释放。" << endl;
    }
};

int main() {
    Array arr(10);
    // 使用数组...
    return 0;
}

5. 注意事项

  • 析构函数必须为公有成员函数,否则对象在离开作用域时无法正确销毁
  • 避免在析构函数中抛出异常,这可能导致程序不可预测的行为

四、拷贝构造函数

1. 什么是拷贝构造函数?

拷贝构造函数(Copy Constructor)是使用同类的另一个对象来初始化新对象时调用的构造函数。它用于定义对象的拷贝行为

2. 语法

ClassName(const ClassName& other);

参数为同类对象的引用,通常为const引用,避免不必要的拷贝

3. 默认拷贝构造函数

  • 如果程序员未提供拷贝构造函数,编译器会自动生成默认的拷贝构造函数,执行浅拷贝
  • 对于没有动态内存分配的类,默认的拷贝构造函数通常够用

4. 浅拷贝与深拷贝

  • 浅拷贝(Shallow Copy):拷贝对象的成员变量值,对于指针成员,仅拷贝指针值,两个对象指向同一内存位置
  • 深拷贝(Deep Copy):在拷贝指针成员时,为新对象分配独立的内存空间,并复制内容

5. 示例

1.默认拷贝构造函数(浅拷贝)

#include <iostream>
using namespace std;

class Shallow {
public:
    int* data;

    Shallow(int val) {
        data = new int(val);
    }

    ~Shallow() {
        delete data;
    }
};

int main() {
    Shallow obj1(5);
    Shallow obj2 = obj1; // 调用默认拷贝构造函数

    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    // 修改obj1的数据
    *(obj1.data) = 10;
    cout << "修改obj1后:" << endl;
    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    return 0;
}

问题:

由于是浅拷贝,obj1obj2data指向同一内存,当一个对象被析构时,内存被释放,另一个对象再使用时会导致悬空指针

2.自定义拷贝构造函数(深拷贝)

#include <iostream>
using namespace std;

class Deep {
public:
    int* data;

    Deep(int val) {
        data = new int(val);
    }

    // 自定义拷贝构造函数
    Deep(const Deep& other) {
        data = new int(*(other.data));
    }

    ~Deep() {
        delete data;
    }
};

int main() {
    Deep obj1(5);
    Deep obj2 = obj1; // 调用自定义拷贝构造函数

    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    // 修改obj1的数据
    *(obj1.data) = 10;
    cout << "修改obj1后:" << endl;
    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    return 0;
}

6. 注意事项

  • 拷贝构造函数的参数必须是引用,否则会导致无限递归
  • 当类中有指针成员,且需要独立的内存空间,必须提供自定义的拷贝构造函数(深拷贝)

五、赋值运算符函数

1. 什么是赋值运算符函数?

赋值运算符函数(Assignment Operator Function)用于定义对象间赋值行为(operator=)。类似于拷贝构造函数,它也需要考虑浅拷贝和深拷贝

2. 语法

ClassName& operator=(const ClassName& other);
  • 返回类型为引用,返回当前对象自身*this,以支持链式赋值
  • 参数为同类对象的**const引用**

3. 默认赋值运算符

  • 如果程序员未提供赋值运算符函数,编译器会生成默认的赋值运算符,执行浅拷贝

4. 示例

1.默认赋值运算符(浅拷贝)

#include <iostream>
using namespace std;

class Shallow {
public:
    int* data;

    Shallow(int val) {
        data = new int(val);
    }

    ~Shallow() {
        delete data;
    }
};

int main() {
    Shallow obj1(5);
    Shallow obj2(10);

    obj2 = obj1; // 使用默认赋值运算符

    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    // 修改obj1的数据
    *(obj1.data) = 15;
    cout << "修改obj1后:" << endl;
    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    return 0;
}

2.自定义赋值运算符函数(深拷贝)

#include <iostream>
using namespace std;

class Deep {
public:
    int* data;

    Deep(int val) {
        data = new int(val);
    }

    Deep(const Deep& other) {
        data = new int(*(other.data));
    }

    // 自定义赋值运算符函数
    Deep& operator=(const Deep& other) {
        if (this == &other) {
            return *this; // 检查自赋值
        }

        delete data; // 释放原有内存
        data = new int(*(other.data)); // 分配新内存并拷贝
        return *this;
    }

    ~Deep() {
        delete data;
    }
};

int main() {
    Deep obj1(5);
    Deep obj2(10);

    obj2 = obj1; // 使用自定义赋值运算符

    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    // 修改obj1的数据
    *(obj1.data) = 15;
    cout << "修改obj1后:" << endl;
    cout << "obj1.data = " << *(obj1.data) << endl;
    cout << "obj2.data = " << *(obj2.data) << endl;

    return 0;
}

5. 注意事项

  • 检查自赋值:在赋值运算符函数中,应检查thisother是否为同一对象,避免释放自己
  • 释放原有资源:在进行赋值前,应释放对象原有的资源,防止内存泄漏
  • 返回*this的引用,支持链式赋值

六、const成员函数

1. 什么是const成员函数?

const成员函数是指在函数声明后加上const关键字的成员函数,表示该函数不会修改对象的成员变量(除非成员变量被声明为mutable

2. 语法

返回类型 函数名(参数列表) const;

3. 特点

  • const成员函数只能调用其他const成员函数,不能调用非const成员函数
  • 可以被const对象调用,而非const成员函数不能被const对象调用

4. 代码示例

#include <iostream>
using namespace std;

class Sample {
private:
    int value;

public:
    Sample(int v) : value(v) {}

    int getValue() const {
        return value;
    }

    void setValue(int v) {
        value = v;
    }
};

int main() {
    const Sample s(10); // 常量对象
    cout << "值是:" << s.getValue() << endl;

    // s.setValue(20); // 错误,不能调用非const成员函数

    return 0;
}

5. const对象和成员函数

  • **const对象:**对象被声明为const,只能调用其const成员函数,不能修改成员变量
  • **非const对象:**可以调用所有成员函数,包括const和非const

6. 成员函数重载

可以根据const性对成员函数进行重载
代码示例:

#include <iostream>
using namespace std;

class Example {
public:
    void func() {
        cout << "非const版本的func()" << endl;
    }

    void func() const {
        cout << "const版本的func()" << endl;
    }
};

int main() {
    Example e;
    e.func(); // 调用非const版本

    const Example ce;
    ce.func(); // 调用const版本

    return 0;
}

七、取地址及const取地址操作符重载

1. 取地址操作符operator&

默认情况下,对象的取地址操作会返回对象的内存地址。可以通过重载operator&来改变取地址操作的行为

2. 为什么需要重载取地址操作符?

  • 在某些情况下,我们希望隐藏对象的内部地址,或提供特殊的地址计算方式
  • 可以用于智能指针的实现

3. 语法

ClassName* operator&();
const ClassName* operator&() const;

4. 示例

1.重载取地址操作符

#include <iostream>
using namespace std;

class MyClass {
public:
    int value;

    MyClass(int v) : value(v) {}

    // 重载取地址操作符
    MyClass* operator&() {
        cout << "自定义取地址操作符被调用。" << endl;
        return this;
    }

    // 重载const版本
    const MyClass* operator&() const {
        cout << "自定义const取地址操作符被调用。" << endl;
        return this;
    }
};

int main() {
    MyClass obj(10);
    MyClass* ptr = &obj; // 调用自定义取地址操作符
    cout << "value = " << ptr->value << endl;

    const MyClass cobj(20);
    const MyClass* cptr = &cobj; // 调用自定义const取地址操作符
    cout << "const value = " << cptr->value << endl;

    return 0;
}

5. 注意事项

  • 小心使用:重载取地址操作符可能导致代码难以理解和维护,应该谨慎使用
  • 避免陷阱:重载后,可能会影响模板代码或标准库的使用,需要确保兼容性

http://www.kler.cn/a/371433.html

相关文章:

  • ZooKeeper的应用场景:深入探讨分布式系统中的多样化应用
  • 详细指南:解决Garmin 手表无法与电脑连接的问题
  • Flutter Image和Text图文组件实战案例
  • 泛微开发修炼之旅--47ecology刷组织机构人员信息中排序字段的解决方案及源码
  • 【python GUI编码入门-01】Python Tkinter入门:从零开始创建GUI应用
  • 【Python爬虫实战】深入解析 Selenium:从元素定位到节点交互的完整自动化指南
  • [ 问题解决篇 ] 解决远程桌面安全登录框的问题
  • 3个模型的交互式多模型IMM,基于EKF的目标跟踪实例(附MATLAB代码)
  • java web调试时清理当前网址的缓存
  • 3.1.3 虚存页面的映射
  • Java学习笔记(九)
  • 云原生后端:现代应用架构的核心力量
  • JavaWeb——Web入门(2/9)-SpringBootWeb:快速入门(入门程序需求、开发步骤、项目相关文件说明、小结)
  • 【Linux网络编程】 --- Linux权限理解
  • ICM20948 DMP代码详解(106)
  • 四、Hadoop 命令高级用法深度剖析
  • 前端之html(二)加入css开篇(一)
  • LeetCode72:编辑距离
  • javaScript中复制一个数组的浅拷贝和深拷贝方法
  • Flutter Web部署到子路径的打包指令
  • 单细胞数据分析(四):细胞亚型注释
  • uniapp写抖音小程序阻止右滑返回上一个页面
  • Vue3使用AntV | X6绘制流程图:开箱即用
  • MPSK(BPSK/QPSK/8PSK)调制解调的Matlab仿真全套
  • TensorFlow面试整理-分布式
  • C语言——linux编程(上)