C++四大构造函数,析构函数详解以及代码示例

发布时间:2023年12月24日

构造函数与析构函数

构造函数是用来初始化对象的成员变量的特殊函数。当创建类的新对象时,构造函数会被自动调用。构造函数的名称与类名相同,并且没有返回类型。一个类可以有多个构造函数,这被称为构造函数的重载。析构函数是当对象的生命周期结束时自动调用的特殊函数,用于执行清理操作,如释放资源、内存等。析构函数的名称由波浪号(~)后跟类名组成,并且没有参数和返回类型。

#include <iostream>

class SimpleClass {
private:
    int number;  // 数据成员

public:
    // 默认构造函数
    SimpleClass() : number(0) { // 初始化为0
        std::cout << "Default constructor called for object at [" << this << "], number = " << number << std::endl;
    }

    // 参数化构造函数
    SimpleClass(int num) : number(num) { // 初始化为传入的值
        std::cout << "Parameterized constructor called for object at [" << this << "], number = " << number << std::endl;
    }

    // 拷贝构造函数
    SimpleClass(const SimpleClass& other) : number(other.number) { // 通过复制来初始化
        std::cout << "Copy constructor called for object at [" << this << "], number = " << number << std::endl;
    }

    // 移动构造函数
    SimpleClass(SimpleClass&& other) noexcept : number(other.number) { // 通过移动来初始化
        other.number = 0; // 移动后,将源对象的数据成员设置为默认值
        std::cout << "Move constructor called for object at [" << this << "], number = " << number << std::endl;
    }

    // 析构函数
    ~SimpleClass() {
        std::cout << "Destructor called for object at [" << this << "], number was " << number << std::endl; // 使用this指针
    }
};

int main() {
    std::cout << "Creating object with default constructor:" << std::endl;
    SimpleClass obj1;

    std::cout << "\nCreating object with parameterized constructor:" << std::endl;
    SimpleClass obj2(42);

    std::cout << "\nCreating object with copy constructor:" << std::endl;
    SimpleClass obj3 = obj2;

    std::cout << "\nCreating object with move constructor:" << std::endl;
    SimpleClass obj4 = std::move(obj2); // obj2的内容现在已经移动到obj4,obj2不应再被使用

    std::cout << "\nExiting main function, objects will be destroyed in reverse order of creation." << std::endl;

    return 0; // 对象会按照创建的相反顺序被销毁: obj4, obj3, obj2, obj1
}

输出结果:

Creating object with default constructor:
Default constructor called for object at [0x61ff0c], number = 0

Creating object with parameterized constructor:

Parameterized constructor called for object at [0x61ff08], number = 42

Creating object with copy constructor:
Copy constructor called for object at [0x61ff04], number = 42

Creating object with move constructor:
Move constructor called for object at [0x61ff00], number = 42

Exiting main function, objects will be destroyed in reverse order of creation.
Destructor called for object at [0x61ff00], number was 42
Destructor called for object at [0x61ff04], number was 42
Destructor called for object at [0x61ff08], number was 0
Destructor called for object at [0x61ff0c], number was 0

在上方的代码中,SimpleClass 类包含了一个整型成员 number 和四种构造函数以及一个析构函数:

  • 默认构造函数:没有参数,将 number 初始化为0,并打印出相关信息。
  • 参数化构造函数:接受一个整数参数,并用该参数初始化number,然后打印出相关信息。
  • 拷贝构造函数:接受一个 SimpleClass 类型的常量引用作为参数,用其 number成员初始化新对象的 number成员,并打印出相关信息。
  • 移动构造函数:接受一个 SimpleClass 类型的右值引用作为参数,将其number 成员的值移动到新对象,并将原对象的 number 成员设为0,然后打印出相关信息。
  • 析构函数:打印出对象被销毁的信息,并显示number 成员的值。

在 main 函数中,创建了四个 SimpleClass 类型的对象,每个对象都使用不同的构造函数。随后,在程序退出时,这些对象按照创建的相反顺序被销毁,析构函数被调用,并打印出相关信息。

注意,由于 obj2 的内容被移动到了 obj4,所以 obj2 的 number 成员在析构时是0。

对比拷贝构造函数和移动构造函数

  • 拷贝构造函数:
    拷贝构造函数用于创建一个类的对象作为另一个同类型对象的副本。它接受一个同类型对象的引用作为参数。拷贝构造函数通常执行深拷贝,即不仅复制对象的值,还复制对象所拥有的资源(如动态分配的内存)。这种构造函数对于管理动态资源和避免悬挂指针问题非常重要。
  • 移动构造函数:
    移动构造函数是C++11引入的新特性,用于支持对象的移动语义。移动构造函数接受一个同类型对象的右值引用作为参数。它允许资源的所有权从一个对象转移到另一个对象,通常涉及指针的简单交换和避免不必要的资源复制。移动构造函数可以显著提高性能,特别是在处理大型对象或资源密集型对象时。
#include <iostream>
#include <chrono>

class Vector {
private:
    int* data;
    size_t size;

public:
    Vector(size_t n) : size(n) {
        data = new int[size];
        for (size_t i = 0; i < size; ++i) {
            data[i] = i;
        }
    }

    // 拷贝构造函数
    Vector(const Vector& other) : size(other.size) {
        data = new int[size];
        for (size_t i = 0; i < size; ++i) {
            data[i] = other.data[i];
        }
    }

    // 移动构造函数
    Vector(Vector&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }

    ~Vector() {
        delete[] data;
    }
};

int main() {
    Vector v1(1000000);

    // 使用拷贝构造函数
    auto startCopy = std::chrono::high_resolution_clock::now();
    Vector v2 = v1;
    auto endCopy = std::chrono::high_resolution_clock::now();

    // 使用移动构造函数
    auto startMove = std::chrono::high_resolution_clock::now();
    Vector v3 = std::move(v1);
    auto endMove = std::chrono::high_resolution_clock::now();

    auto durationCopy = std::chrono::duration_cast<std::chrono::microseconds>(endCopy - startCopy).count();
    auto durationMove = std::chrono::duration_cast<std::chrono::microseconds>(endMove - startMove).count();

    std::cout << "Time taken by copy constructor: " << durationCopy << " microseconds" << std::endl;
    std::cout << "Time taken by move constructor: " << durationMove << " microseconds" << std::endl;

    return 0;
}

输出结果:

Time taken by copy constructor: 1651 microseconds
Time taken by move constructor: 0 microseconds

在上方的main函数中,首先创建了一个大型Vector对象v1,然后分别使用拷贝构造函数和移动构造函数创建了两个新对象v2和v3。使用了std::chrono库来测量拷贝和移动操作所需的时间。由于移动构造函数避免了数据的复制,所以它的执行时间远远少于拷贝构造函数。

输出结果显示,拷贝构造函数用时1651微秒,而移动构造函数几乎不需要时间(0微秒),展示了移动语义在处理大型对象时的性能优势。

文章来源:https://blog.csdn.net/qq_46348508/article/details/135178779
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。