【C++】构造函数,析构函数和拷贝构造函数的区别


构造函数,析构函数和拷贝构造函数的区别

构造函数(Constructor)和析构函数(Destructor)是面向对象编程中两个非常重要的概念,它们分别用于对象的初始化和清理工作。

构造函数:

构造函数是一种特殊的成员函数,它在创建对象时自动调用,用于初始化对象的状态。构造函数的名称与类名相同,并且没有返回类型,甚至连void也不返回。构造函数可以有参数,这样可以在创建对象时传递初始值。

构造函数的主要作用包括:

  1. 初始化对象的成员变量。
  2. 分配资源,如动态内存分配。
  3. 执行其他必要的初始化操作。

构造函数可以有多个,这称为构造函数的重载。根据传递给构造函数的参数不同,可以调用不同的构造函数来创建对象。

示例代码(C++):

class Example {
public:
    int value;
    Example() { // 无参数构造函数
        value = 0;
    }
    Example(int val) { // 带参数的构造函数
        value = val;
    }
};

int main() {
    Example obj1; // 调用无参数构造函数
    Example obj2(10); // 调用带参数的构造函数
    return 0;
}

析构函数:

析构函数是另一个特殊成员函数,它在对象生命周期结束时自动调用,用于清理对象占用的资源,如释放动态分配的内存、关闭文件等。析构函数的名称是类名前加上波浪号(~),同样没有返回类型。

析构函数的主要作用包括:

  1. 清理对象占用的资源。
  2. 执行其他必要的清理操作。

析构函数通常只有一个,因为对象的清理工作通常是一致的。析构函数不能有参数,也不能被显式调用。

示例代码(C++):

class Example {
public:
    int* ptr;
    Example() {
        ptr = new int(10); // 动态分配内存
    }
    ~Example() {
        delete ptr; // 释放内存
    }
};

int main() {
    Example obj; // 创建对象,调用构造函数
    // ... 使用对象
    return 0; // 程序结束时,调用析构函数
}

在上面的示例中,构造函数Example()用于初始化成员变量ptr,而析构函数~Example()则用于在对象生命周期结束时释放ptr指向的内存。

拷贝构造函数(Copy Constructor):

  1. 用于创建一个新对象作为现有对象的副本。
  2. 只有一个,参数是对现有对象的引用。
  3. 通常用于深拷贝,即复制对象的所有成员变量,包括动态分配的内存等。

拷贝构造函数的特殊之处在于它接受一个对现有对象的引用作为参数,并创建一个新的对象作为该对象的副本。拷贝构造函数的目的是确保对象的复制行为符合预期,特别是在处理动态分配的内存时,需要确保内存被正确复制而不是简单地复制指针。

让我们详细解释一下之前提供的示例代码,这段代码是用C++编写的,它展示了构造函数、拷贝构造函数和析构函数的使用。

class Example {
public:
     int* ptr; // 成员变量,一个指向整数的指针

     // 构造函数
     Example() {
         ptr = new int(10); // 动态分配内存,并初始化为10
     }

     // 拷贝构造函数
     Example(const Example& other) {//other是创建的那个对象也是这个拷贝构造函数唯一的一个参数,引用调用这个拷贝构造函数时传入的对象
         //main()里Example obj2 = obj1;说明调用的对象是obj1对象。other没有自己的名字,它只是一个参数,用于在拷贝构造函数中引用传入的对象。
         ptr = new int(*other.ptr); // 深拷贝,创建一个新的整数并复制other.ptr指向的值
/***
	1. other.ptr:这是一个指向整数的指针,它属于Example类的一个实例。在这个上下文中,other是一个引用,它引用了另一个Example对象obj1.
	other.ptr指向的是obj1对象的ptr成员变量所指向整数值。由于other是obj1的引用,所以other.ptr和obj1.ptr指向的是同一个整数。
    2. *other.ptr:这是一个解引用操作,它访问了other.ptr指向的整数值。*操作符用于获取指针指向的值。在这个例子中,它获取了other.ptr指向的整数值。
	3. new int(*other.ptr):这是一个动态分配内存的操作。new关键字用于在堆(heap)上分配内存。这里,它分配了一个新的整数,并用*other.ptr的值初始化这个整数。这意味着新分配的整数的值与other.ptr指向的整数的值相同。【也就是复制了内存的值,两个内存的值一样】
	4. ptr = ...:这是一个赋值操作。ptr是当前对象的成员变量,它是一个指向整数的指针。通过这个赋值操作,ptr被赋予了新分配的整数的地址。【也就是新的内存中的地址】
	
	综上所述,ptr = new int(*other.ptr);这行代码的作用是:
    - 通过other.ptr获取另一个对象的整数值。
    - 使用这个值在堆上动态分配一个新的整数。
    - 将当前对象的ptr指针指向这个新分配的整数。

	这个操作确保了当前对象(obj2)的ptr成员指向一个独立的整数副本,而不是与obj1共享同一个整数。这是深拷贝的一个例子,它确保了每个对象都有自己的独立数据副本,而不是仅仅复制指针值。这样,当对象被销毁时,每个对象都可以独立地释放自己的资源,避免了资源管理上的错误。
***/
         
     }

     // 析构函数
     ~Example() {
         delete ptr; // 释放动态分配的内存
     }
};

int main() {
     Example obj1; // 创建对象obj1,调用构造函数
     Example obj2 = obj1; // 调用拷贝构造函数创建obj2,obj2是obj1的副本

     // ... 在这里可以使用obj1和obj2

     return 0; // 程序结束时,先调用obj2的析构函数,再调用obj1的析构函数
}

让我们逐行解释代码:

  1. class Example { ... }; 定义了一个名为Example的类,它有一个公共成员变量ptr,这是一个指向整数的指针。

  2. Example()Example类的构造函数。当创建Example类的实例时,这个构造函数会被自动调用。在这个构造函数中,我们使用new关键字动态分配了一块内存,并将这块内存的地址赋给了ptr指针。这块内存被初始化为整数值10。

  3. Example(const Example& other)Example类的拷贝构造函数。拷贝构造函数用于创建一个新对象作为现有对象的副本。它接受一个对现有对象的引用作为参数。在这个函数中,我们同样使用new关键字为新对象分配内存,并将现有对象otherptr指向的值复制到新分配的内存中。这样,obj2就成为了obj1的一个独立副本。

  4. ~Example()Example类的析构函数。析构函数在对象生命周期结束时自动调用,用于清理对象占用的资源。在这个析构函数中,我们使用delete关键字释放了ptr指向的动态分配的内存。

  5. int main() { ... } 是程序的入口点。在这个函数中,我们创建了两个Example类的对象:obj1obj2

  6. Example obj1; 创建了obj1对象,调用了构造函数,动态分配了内存,并将内存初始化为10。

  7. Example obj2 = obj1; 创建了obj2对象,调用了拷贝构造函数。obj2obj1的副本,它有自己的独立内存空间。

  8. return 0; 表示程序结束。在程序结束时,会自动调用obj2obj1的析构函数,按照它们被创建的相反顺序进行清理。首先调用obj2的析构函数,释放obj2的内存,然后调用obj1的析构函数,释放obj1的内存。


在这里插入图片描述
觉得有用的点个赞呗~

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值