c++类和对象(2)

时间:2024-12-02 09:40:46

1. 类的6个默认成员函数

在C++中,如果一个类没有显式定义某些成员函数,编译器会默认为这个类生成六个默认成员函数。以下是这六个默认成员函数:

默认构造函数(Default Constructor)

如果类没有定义任何构造函数,编译器会提供一个默认构造函数,这个默认构造函数是一个无参构造函数,它的作用是创建一个对象,并为对象的成员变量提供默认值。

析构函数(Destructor)

如果类没有定义析构函数,编译器会提供一个默认的析构函数,默认析构函数是一个空函数体,用于在对象生命周期结束时释放资源。

拷贝构造函数(Copy Constructor)

用于通过一个同类型的已存在对象来初始化一个正在创建的对象。默认的拷贝构造函数执行成员的逐位复制。

拷贝赋值运算符(Copy Assignment Operator)

用于将一个对象赋值给同类型的另一个已存在对象。默认的拷贝赋值运算符执行成员的逐位赋值。

移动构造函数(Move Constructor) (C++11及以后)

当需要通过一个临时对象来初始化同类型的另一个对象时,移动构造函数会被调用。默认的移动构造函数“窃取”临时对象的资源。

移动赋值运算符(Move Assignment Operator) (C++11及以后)

用于将一个临时对象赋值给同类型的另一个已存在对象。默认的移动赋值运算符也是“窃取”临时对象的资源。

2. 构造函数

构造函数是类的一个特殊成员函数,它的主要作用是在创建类的对象时初始化对象的成员变量。构造函数具有以下特点:

  1. 函数名称:构造函数的名称必须与类名相同。
  2. 参数:构造函数可以没有参数,也可以有多个参数,用于提供初始化成员变量的值。
  3. 返回类型:构造函数没有返回类型,即使是void也不可以。
  4. 作用:在对象创建时自动调用,用于初始化对象的成员变量。

默认构造函数

如果类没有定义任何构造函数,编译器会提供一个默认构造函数。如果定义了其他构造函数,但仍然需要一个无参的构造函数,则必须显式定义默认构造函数。

class MyClass {
public:
    MyClass() {
        // 默认构造函数的实现
    }
};

 参数化构造函数

允许在创建对象时传递参数,以初始化对象的成员变量。

class MyClass {
public:
    int x;
    MyClass(int val) : x(val) {
        // 参数化构造函数的实现
    }
};

 转换构造函数

允许通过一个不同类型的值来创建对象。

class MyClass {
public:
    MyClass(int val) {
        // 转换构造函数的实现
    }
};

 初始化列表

构造函数可以使用初始化列表来初始化成员变量,这通常比在函数体内赋值更高效。

class MyClass {
public:
    int x;
    MyClass() : x(10) {
        // 使用初始化列表
    }
};

3. 析构函数

析构函数是类的一个特殊成员函数,它的作用是在对象生命周期结束时执行必要的清理工作。析构函数具有以下特点:

  1. 函数名称:析构函数的名称是在类名前加上波浪号(~)。
  2. 参数:析构函数没有参数,因此不能被重载。
  3. 返回类型:析构函数没有返回类型,即使是void也不可以。
  4. 作用:在对象被销毁时自动调用,用于释放对象所占用的资源,如动态分配的内存、打开的文件句柄、网络连接等。

规则

  • 每个类只能有一个析构函数。
  • 如果类没有显式定义析构函数,编译器会自动生成一个默认的析构函数,该默认析构函数是一个空函数体。
  • 如果类中包含指向动态分配内存的指针成员,则通常需要定义一个自定义的析构函数来释放这些内存。
class MyClass {
public:
    MyClass() {
        // 构造函数的实现,可能包含动态内存分配
    }
    
    ~MyClass() {
        // 析构函数的实现,释放动态分配的内存
    }
private:
    int* data; // 指向动态分配内存的指针
};

int main() {
    MyClass obj; // 创建对象,调用构造函数
    // ... 使用对象 ...
    return 0; // 程序结束,obj对象生命周期结束,调用析构函数
}

4. 拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它用于创建一个新对象作为另一个同类对象的副本。拷贝构造函数具有以下特点:

  1. 函数名称:拷贝构造函数的名称与类名相同,且有一个参数,该参数是对类类型对象的引用。
  2. 参数:拷贝构造函数的参数必须是类类型的引用,通常使用常量引用,以避免修改原对象,并允许传递临时对象。
  3. 返回类型:拷贝构造函数没有返回类型,即使是void也不可以。
  4. 作用:当需要通过已存在的对象来初始化一个新对象时,拷贝构造函数会被调用。
class MyClass {
public:
    MyClass(const MyClass& other) {
        // 拷贝构造函数的实现
        // 通常用于复制成员变量
    }
    
    // 其他成员函数和成员变量...
};

默认拷贝构造函数

如果类没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。默认拷贝构造函数执行成员的逐位复制(浅拷贝)。对于包含指针成员的类,默认拷贝构造函数可能会导致问题,因为它只会复制指针的值,而不是指针指向的数据。

自定义拷贝构造函数

在某些情况下,需要自定义拷贝构造函数来执行深拷贝,以确保每个对象都有自己独立的副本。

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}
    
    // 自定义拷贝构造函数
    MyClass(const MyClass& other) : data(new int(*other.data)) {
        // 执行深拷贝
    }
    
    ~MyClass() {
        delete data; // 释放动态分配的内存
    }
};

5. 赋值运算符重载

在C++中,赋值运算符 = 可以被重载以定制对象之间的赋值行为。当你定义了一个类,并且希望对象能够使用 = 运算符进行赋值时,你可能需要重载赋值运算符。

  1. 参数类型:赋值运算符重载函数应该有一个参数,该参数是类类型的引用。通常使用常量引用,以允许传递临时对象,并防止在赋值过程中修改右操作数。

  2. 返回类型:赋值运算符重载函数应该返回一个指向当前对象的引用,通常是一个 *this 指针。

  3. 函数名称:赋值运算符重载的函数名是 operator=

  4. 成员函数:赋值运算符必须是类的成员函数。

class MyClass {
public:
    int* data;

    MyClass(int value) : data(new int(value)) {}

    // 赋值运算符重载
    MyClass& operator=(const MyClass& other) {
        if (this != &other) { // 避免自我赋值
            delete data;      // 删除旧值
            data = new int(*other.data); // 深拷贝
        }
        return *this; // 返回当前对象的引用
    }

    ~MyClass() {
        delete data; // 析构函数中释放动态分配的内存
    }
};

 

注意事项:

  • 自我赋值检查:在赋值运算符的实现中,检查自我赋值是非常重要的,以避免在释放内存后使用悬空指针。
  • 资源管理:如果类管理动态分配的资源,赋值运算符应该正确地管理这些资源,避免内存泄漏和悬挂指针。
  • 返回值:赋值运算符应该返回当前对象的引用,这允许链式赋值,例如 a = b = c;

6. const成员函数

在C++中,const成员函数是指那些承诺不修改调用对象的数据成员的函数。当你声明一个成员函数为const时,你告诉编译器这个函数不会改变对象的任何成员变量(非静态成员变量)。这是通过在函数声明和定义后面加上const关键字来实现的。

class MyClass {
public:
    void normalFunction() {
        // 可以修改成员变量
    }

    void constFunction() const {
        // 不能修改成员变量
    }
};

 

注意事项

  • const成员函数内部,你不能修改任何非静态成员变量。
  • 你可以修改静态成员变量和局部变量,因为这些变量不与对象的状态直接相关。
  • 你可以在const成员函数内部调用其他非const成员函数,但前提是这些函数不修改任何非静态成员变量。

7. 取地址及const取地址操作符重载

在C++中,你可以重载几种特殊的成员操作符,包括取地址操作符 & 和 const 取地址操作符 const &。虽然这些操作符通常不需要重载,因为编译器默认提供的版本就已经足够使用,但在某些特殊情况下,你可能需要自定义它们的行为。

重载取地址操作符 &

class MyClass {
public:
    // 重载取地址操作符
    MyClass* operator&() {
        // 返回当前对象的地址
        return this;
    }
};

重载const取地址操作符 const &

class MyClass {
public:
    // 重载const取地址操作符
    const MyClass* operator&() const {
        // 返回当前对象的const地址
        return this;
    }
};

注意事项

  • 重载取地址操作符通常不是一个好主意,因为这可能会导致混淆和意外的行为。通常,编译器提供的默认行为是正确的,即返回对象的实际地址。
  • 如果你确实重载了这些操作符,它们必须返回指向类类型的指针。对于 const 版本,返回类型应该是指向 const 类型的指针。
  • 重载这些操作符的返回类型不能是引用类型,因为地址操作符的预期行为是返回一个指针。
  • 重载这些操作符的目的是为了改变它们的行为,但通常不建议这样做,除非你有非常特殊的需求。