1.拷贝构造函数(Copy Constructor)
是C++中的特殊成员函数,用于创建一个新对象,并将现有对象的值复制到新对象中。它通常用于在以下情况下创建对象:
- 通过一个已存在的对象初始化一个新对象。
- 将对象作为参数传递给函数。
- 从函数返回一个对象。
拷贝构造函数的定义形式如下:
class MyClass {
public:
MyClass(); // 默认构造函数
MyClass(const MyClass& other); // 拷贝构造函数
// ...
};
拷贝构造函数的参数是对同类型的另一个对象的常引用(const MyClass&
)。在创建新对象时,拷贝构造函数会将现有对象的属性值复制到新对象中。
当需要使用拷贝构造函数时,C++会自动调用已定义的拷贝构造函数,例如:
MyClass obj1; // 调用默认构造函数创建一个对象
MyClass obj2(obj1); // 调用拷贝构造函数,将obj1的值复制到obj2中
在上面的示例中,obj2
是通过拷贝构造函数将obj1
的值复制到新对象中创建的。需要注意的是,拷贝构造函数是以引用方式传递参数的,这样可以避免不必要的对象拷贝,提高性能。
如果在类定义中未显式定义拷贝构造函数,编译器会提供一个默认的拷贝构造函数。默认的拷贝构造函数会按成员变量的顺序逐个进行拷贝。这种默认的拷贝构造函数适用于大多数情况,但如果类包含指针成员或其他需要特殊处理的资源,通常需要显式定义拷贝构造函数,以确保正确地复制对象的值。
需要注意的是,在某些情况下,可以通过显式删除拷贝构造函数来禁止对象的拷贝,或通过自定义实现来实现特定的拷贝行为。
2.析构函数
析构函数(Destructor)是C++中的特殊成员函数,用于在对象销毁时执行清理操作和释放资源。它的主要功能是回收由对象在其生命周期中分配的资源,如堆内存、打开的文件、网络连接等。
析构函数的名称与类名相同,但在前面加上一个波浪线(~),没有参数和返回类型。它的定义形式如下:
class MyClass {
public:
MyClass(); // 构造函数
~MyClass(); // 析构函数
// ...
};
当一个对象的生命周期结束(销毁)时,C++编译器会自动调用对象的析构函数。析构函数的调用时机有几种情况:
- 对象的作用域结束(退出其定义所在的区域);
- 动态分配的对象通过
delete
操作符显式地释放; - 局部对象在函数返回后自动销毁;
- 对象是全局变量,程序终止时销毁。
析构函数的主要功能可以有多种应用场景,如关闭文件、释放内存、断开连接等。以下是一个简单的示例,演示了析构函数的基本用法:
#include <iostream>
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
int main() {
{
Resource obj; // 创建 Resource 对象
} // 对象在作用域结束时销毁
Resource* ptr = new Resource(); // 动态分配的对象
delete ptr; // 释放对象
return 0;
}
在上述示例中,对象obj
在其作用域结束时,会自动调用析构函数进行资源的释放。而动态分配的对象ptr
需要显式调用delete
释放,这样析构函数才会被调用。
需要注意的是,析构函数的定义应该与构造函数的实现一致,以确保资源的正确释放。此外,当一个类需要显式地管理资源的时候,应该实现自定义的析构函数,进行资源回收。
3.为什么析构函数一般写成虚函数
由于类的多态性,基类指针可以指向派生类的对象,如果删除该基类的指针,就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放.
如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全,造成内存泄漏。
所以将析构函数声明为虚函数是十分必要的。在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生,要将基类的析构函数声明为虚函数。
析构函数一般写成虚函数的原因是为了实现多态性(Polymorphism)和安全的基类指针删除。
-
多态性:在面向对象编程中,多态性是指通过基类指针或引用调用派生类对象的成员函数。当基类指针指向派生类对象时,如果基类的析构函数不是虚函数,那么当通过基类指针删除对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这样就无法正确释放派生类对象中分配的资源,可能导致资源泄漏和未定义行为。通过将析构函数声明为虚函数,可以实现正确的多态性析构,即在删除对象时,会先调用派生类的析构函数,然后再调用基类的析构函数。
-
安全的基类指针删除:当使用基类指针指向派生类对象时,如果基类的析构函数不是虚函数,那么当通过基类指针删除对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这样会导致派生类对象无法正确地释放资源,可能会造成内存泄漏或资源泄漏。通过将析构函数声明为虚函数,可以确保在通过基类指针删除对象时,会依次调用基类和派生类的析构函数,从而正确释放对象及其资源。
因此,将析构函数声明为虚函数是一种良好的编程实践,尤其是当使用多态性和基类指针删除对象时,可以确保正确的对象析构和资源释放。