第九章:C++构造函数和析构函数详解
1. 概述
在C++中,构造函数和析构函数是用于初始化对象和清理对象资源的特殊成员函数。构造函数负责完成对象的初始化工作,而析构函数则负责在对象生命周期结束时清理资源。
2. 构造函数
构造函数是一种特殊的成员函数,与类名相同且没有返回类型。它在创建对象时被自动调用,用于执行必要的初始化操作。
2.1 构造函数的定义
每个类可以有一个或多个构造函数。根据参数列表的不同,构造函数可以分为无参构造函数和有参构造函数。
class MyClass {
public:
// 无参构造函数
MyClass() {
// 初始化代码
}
// 有参构造函数
MyClass(int value) {
// 初始化代码
}
};
2.2 构造函数的调用
构造函数在创建对象时被自动调用,不需要显式调用构造函数。当创建对象时,编译器会根据提供的参数匹配合适的构造函数进行调用。
MyClass obj1; // 调用无参构造函数创建对象
MyClass obj2(100); // 调用有参构造函数创建对象
2.3 初始化列表
构造函数的初始化列表可以用于对成员变量进行初始化。使用初始化列表可以提供更高效的初始化方法,并确保成员变量按正确的顺序初始化。
class MyClass {
private:
int value;
double scale;
public:
MyClass(int v, double s) : value(v), scale(s) {
// 初始化代码
}
};
2.4 默认构造函数
如果没有为类定义任何构造函数,编译器会自动生成一个默认构造函数。默认构造函数不接受任何参数,也不执行任何初始化操作。但一旦自定义了其他构造函数,编译器将不再生成默认构造函数。
class MyClass {
public:
// 自定义构造函数
MyClass(int value) {
// 初始化代码
}
// 编译器不会生成默认构造函数
};
2.5 拷贝构造函数
拷贝构造函数是一种特殊的构造函数,用于创建一个新对象并使用已有对象进行初始化。它可以通过值传递或引用传递来调用。
class MyClass {
public:
// 拷贝构造函数
MyClass(const MyClass& other) {
// 初始化代码,使用other对象的值来初始化新的对象
}
};
MyClass obj1; // 调用无参构造函数创建对象obj1
MyClass obj2(obj1); // 调用拷贝构造函数创建对象obj2,使用obj1的值来初始化
3. 析构函数
析构函数是一种特殊的成员函数,与类名相同但在前面加上波浪号(~)。它用于释放对象占用的资源。
3.1 析构函数的定义
每个类只能有一个析构函数,没有返回类型,也不接受任何参数。
class MyClass {
public:
// 析构函数
~MyClass() {
// 清理资源的代码
}
};
3.2 析构函数的调用
析构函数在对象被销毁时自动调用,无法手动显式调用。当对象超过其作用域、删除对象的指针或程序结束时,析构函数会被调用。
void Function() {
MyClass obj; // 对象obj在该函数块结束时将被销毁,自动调用析构函数
}
int main() {
MyClass* ptr = new MyClass(); // 使用new动态创建对象,需要手动delete释放内存
delete ptr; // 调用析构函数后释放内存
return 0;
}
3.3 析构函数的应用
析构函数经常用于释放对象占用的资源,如内存、文件句柄、网络连接等。通过在析构函数中执行必要的清理操作,可以避免资源泄漏。
class FileManager {
private:
FILE* file;
public:
FileManager(const char* filename) {
file = fopen(filename, "r"); // 打开文件
if (!file) {
// 处理打开文件失败的情况
}
}
~FileManager() {
if (file) {
fclose(file); // 关闭文件
}
}
};
4. 示例案例:图书管理系统
下面是一个简单的图书管理系统示例,用于展示构造函数和析构函数的实际应用。
#include <iostream>
#include <vector>
using namespace std;
// Book类表示图书对象
class Book {
private:
string title;
string author;
public:
Book(const string& t, const string& a) : title(t), author(a) {
cout << "Book \"" << title << "\" by " << author << " created." << endl;
}
~Book() {
cout << "Book \"" << title << "\" by " << author << " destroyed." << endl;
}
void display() {
cout << "Title: " << title << endl;
cout << "Author: " << author << endl;
}
};
// Library类表示图书馆
class Library {
private:
vector<Book> books;
public:
void addBook(const Book& book) {
books.push_back(book);
}
void displayAllBooks() {
for (const auto& book : books) {
book.display();
cout << endl;
}
}
};
int main() {
Library library;
library.addBook(Book("Harry Potter", "J.K. Rowling"));
library.addBook(Book("To Kill a Mockingbird", "Harper Lee"));
library.displayAllBooks();
return 0;
}
运行结果:
Book "Harry Potter" by J.K. Rowling created.
Book "To Kill a Mockingbird" by Harper Lee created.
Title: Harry Potter
Author: J.K. Rowling
Title: To Kill a Mockingbird
Author: Harper Lee
Book "Harry Potter" by J.K. Rowling destroyed.
Book "To Kill a Mockingbird" by Harper Lee destroyed.
运行以上示例代码,我们可以看到构造函数和析构函数的调用情况。在创建图书对象时,构造函数被调用,初始化了对象的成员变量,并输出了相应的提示信息。而当程序结束时,或者从图书馆中删除图书对象时,析构函数会被调用,释放了对象占用的资源,并输出了相应的销毁信息。