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++中是管理对象生命周期和资源的重要工具。熟练掌握它们的使用可以有效避免资源泄漏、内存管理错误等问题。