一、什么是构造函数与析构函数
在C++中,对象的创建和销毁都是由构造函数和析构函数控制的。构造函数是在对象创建时被调用,用于初始化对象的属性和数据成员;析构函数则是在对象销毁时被调用,用于清理对象占用的资源。
构造函数和析构函数是类的成员函数,没有返回值,函数名必须与类名相同。一个类可以有多个构造函数,但只能有一个析构函数。
下面是一个简单的示例代码:
class Person { public: Person() { // 默认构造函数 name = ""; age = 0; std::cout << "Person对象被创建" << std::endl; } Person(std::string name, int age) { // 有参构造函数 this->name = name; this->age = age; std::cout << "Person对象被创建" << std::endl; } ~Person() { // 析构函数 std::cout << "Person对象被销毁" << std::endl; } private: std::string name; int age; };
二、构造函数
构造函数可以分为默认构造函数和有参构造函数。默认构造函数不带任何参数,用于创建对象时对数据成员进行默认初始化。有参构造函数则接收参数,在创建对象时进行属性的初始化。
下面是一个默认构造函数和有参构造函数的示例代码:
class Person { public: Person() { // 默认构造函数 name = ""; age = 0; } Person(std::string name, int age) { // 有参构造函数 this->name = name; this->age = age; } private: std::string name; int age; }; int main() { Person p1; // 调用默认构造函数 Person p2("Tom", 20); // 调用有参构造函数 return 0; }
需要注意的是,如果一个类定义了有参构造函数,那么默认构造函数不会被自动创建。如果需要使用默认构造函数,就必须自己定义。
另外,可以使用初始化列表来对数据成员进行初始化,这样效率更高,代码也更简洁。下面是初始化列表的示例代码:
class Person { public: Person() : name(""), age(0) {} // 初始化列表 Person(std::string name, int age) : name(name), age(age) {} private: std::string name; int age; };
三、析构函数
析构函数是对象销毁时自动被调用的函数,用于清理对象使用的资源,如动态分配的内存空间、打开的文件等。
下面是一个示例代码,展示在何时调用析构函数:
class Person { public: Person() { std::cout << "Person对象被创建" << std::endl; } ~Person() { std::cout << "Person对象被销毁" << std::endl; } }; int main() { Person p1; // 调用构造函数,输出 "Person对象被创建" { Person p2; // 调用构造函数,输出 "Person对象被创建" } // 调用析构函数,输出 "Person对象被销毁" return 0; // 调用析构函数,输出 "Person对象被销毁" }
可以看到,当一个对象的作用域结束时,其析构函数就会被调用。在上面的示例代码中,p2的作用域在{}中,当{}结束时,p2就会被销毁。
四、拷贝构造函数
拷贝构造函数是用于在创建一个对象时,以已经存在的对象作为初始化来源。拷贝构造函数可以通过传递引用或指针的方式进行调用。
如果我们没有定义拷贝构造函数,C++会为我们生成一个默认的拷贝构造函数。但是如果类中包含了动态分配的内存空间时,需要我们手动定义拷贝构造函数,以确保正确地复制对象。
下面是一个手动定义拷贝构造函数的示例代码:
class Person { public: Person() { } Person(std::string name, int age) : name(name), age(age) { } Person(const Person& p) { // 拷贝构造函数 name = p.name; age = p.age; } private: std::string name; int age; }; int main() { Person p1("Tom", 20); Person p2(p1); // 调用拷贝构造函数 return 0; }
五、移动构造函数和移动赋值运算符
移动构造函数和移动赋值运算符是C++11引入的新特性。它们的作用是将一个对象的资源转移到另一个对象中,避免了对象的拷贝,提高了程序的效率。
移动构造函数用于在创建一个对象时将另一个对象的资源“移动”到新对象中;移动赋值运算符用于将一个对象的资源“移动”到另一个对象中。移动构造函数和移动赋值运算符都可以通过右值引用的方式进行调用。
下面是一个示例代码,展示如何使用移动构造函数和移动赋值运算符:
class Vector { public: Vector() : data(nullptr), size(0) {} Vector(const Vector& other) { // 拷贝构造函数 size = other.size; data = new int[size]; for (int i = 0; i < size; i++) { data[i] = other.data[i]; } } Vector(Vector&& other) { // 移动构造函数 data = other.data; size = other.size; other.data = nullptr; other.size = 0; } Vector& operator=(const Vector& other) { // 拷贝赋值运算符 delete[] data; size = other.size; data = new int[size]; for (int i = 0; i < size; i++) { data[i] = other.data[i]; } return *this; } Vector& operator=(Vector&& other) { // 移动赋值运算符 delete[] data; data = other.data; size = other.size; other.data = nullptr; other.size = 0; return *this; } private: int* data; size_t size; }; int main() { Vector v1; Vector v2 = std::move(v1); // 使用移动构造函数 Vector v3; v3 = std::move(v2); // 使用移动赋值运算符 return 0; }
总结
本文介绍了C++中的构造函数和析构函数,以及其一些衍生的概念如拷贝构造函数和移动构造函数。掌握这些概念有助于我们更好地理解C++中对象的初始化和清理过程,并编写高效、安全的代码。