C++ 构造函数和析构函数

1. 构造函数(Constructor)

构造函数的主要功能是初始化对象的状态,它在对象创建时自动调用。构造函数可以被重载,也可以使用默认构造函数。

1: 基本语法与规则

构造函数的名称:构造函数的名称必须与类名相同,且没有返回值类型。

默认构造函数:没有参数的构造函数称为默认构造函数。如果类中没有定义任何构造函数,编译器会自动生成一个默认构造函数。

MyClass obj;  // 调用默认构造函数
重载构造函数:构造函数可以被重载,这意味着一个类可以有多个构造函数,只要它们的参数列表不同。

class MyClass {
public:
    MyClass() { }                     // 默认构造函数
    MyClass(int x) { }                // 带参数的构造函数
    MyClass(int x, int y) { }         // 另一个带参数的构造函数
};
 

2: 初始化列表

初始化列表:C++支持在构造函数中使用初始化列表来初始化成员变量,尤其是常量成员变量和引用类型成员变量时,这是必须的。

3: 构造函数调用规则
  • 隐式调用与显式调用:构造函数是隐式调用的,不能手动调用。但可以通过构造对象时传入参数来触发不同的构造函数。

  • MyClass obj1;         // 调用默认构造函数
    MyClass obj2(10);     // 调用带参数的构造函数
     

4: 构造函数与new操作符
  • 使用new操作符动态创建对象时,构造函数会被自动调用,内存分配成功后返回指向该对象的指针。
  • MyClass* obj = new MyClass(10);  // 使用new操作符创建对象
     
5: 匿名对象
  • 使用构造函数可以创建匿名对象,即未分配给任何变量的临时对象,通常用于一次性的操作。
  • MyClass(10);  // 创建匿名对象,调用带参数的构造函数
     
6: 转换构造函数
  • 如果构造函数只有一个参数,它可以被用作隐式类型转换构造函数,这有时会带来问题。可以使用 explicit 关键字来避免隐式转换。
  • class MyClass {
    public:
        MyClass(int x) { }      // 允许隐式转换
    };

    MyClass obj = 10;           // 等效于 MyClass obj(10);

    class SafeClass {
    public:
        explicit SafeClass(int x) { }  // 防止隐式转换
    };

    SafeClass obj2 = 10;        // 错误:explicit阻止隐式转换
     

7: 深拷贝与浅拷贝
  • 深拷贝:当类中有指针等资源需要独立管理时,应该提供一个拷贝构造函数来实现深拷贝,以避免多个对象共享同一块资源。

class MyClass {
private:
    int* data;
public:
    MyClass(int value) {
        data = new int(value);
    }
    // 拷贝构造函数,实现深拷贝
    MyClass(const MyClass& other) {
        data = new int(*(other.data));
    }
    ~MyClass() {
        delete data;
    }
};

2. 析构函数(Destructor)

析构函数的主要功能是释放对象所占用的资源。它在对象销毁时被自动调用。

1: 基本语法与规则
  • 析构函数的名称:析构函数与构造函数相似,名称是类名前加~符号,且没有返回值类型。

  • class MyClass {
    public:
        ~MyClass() {
            // 清理资源
        }
    };

  • 不能重载析构函数:每个类只能有一个析构函数,不能重载。

  • 不能带参数:析构函数不接受参数,因此不能重载。

2: 析构函数调用时机
  • 当对象超出其作用域,或者使用 delete 关键字删除时,析构函数会自动调用。

{
    MyClass obj;  // 超出作用域后调用析构函数
}

MyClass* obj = new MyClass();
delete obj;      // 调用析构函数,并释放动态内存
 

3: 动态分配内存与析构函数
  • 当类使用 new 动态分配内存时,需要在析构函数中使用 delete 来释放内存,避免内存泄漏。

class MyClass {
private:
    int* data;
public:
    MyClass(int value) {
        data = new int(value);
    }
    ~MyClass() {
        delete data;  // 释放动态分配的内存
    }
};
 

4: 析构函数与继承
  • 在有继承关系的类中,基类的析构函数应该定义为 virtual,以确保通过基类指针删除对象时,派生类的析构函数能够被正确调用。

class Base {
public:
    virtual ~Base() {
        std::cout << "Base 析构函数调用" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        std::cout << "Derived 析构函数调用" << std::endl;
    }
};

Base* obj = new Derived();
delete obj;  // 调用 Derived 和 Base 的析构函数
 

5: 析构顺序
  • 当对象的成员是其他类的对象时,析构函数的调用顺序是:首先调用派生类的析构函数,然后按构造顺序逆序调用成员对象的析构函数,最后是基类析构函数。

3. 构造函数和析构函数的结合使用

  • 对象的生命周期管理:构造函数负责对象的创建和初始化,析构函数负责对象的销毁和资源释放。它们共同管理对象的生命周期。
  • RAII(Resource Acquisition Is Initialization):资源获取即初始化。RAII是C++中常用的技术,即将资源的获取与对象的生命周期绑定,利用构造函数获取资源,利用析构函数释放资源,确保程序中不发生资源泄漏。

4. 综合示例

5. 总结

构造函数和析构函数在C++中是管理对象生命周期和资源的重要工具。熟练掌握它们的使用可以有效避免资源泄漏、内存管理错误等问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值