c++ 构造、拷贝构造、析构

本文深入探讨C++中的构造函数、析构函数和拷贝构造函数的特性和使用方法,包括它们的定义、特点及如何避免浅拷贝带来的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概要

本篇文章主要内容有 构造函数、析构函数、拷贝构造函数。

构造函数

类实例化对象时,会调用构造函数。构造函数的目的是声明对象。即使是一个空类也会有编译器自动生成的构造函数。

    #include <iostream>
    using namespace std;
    class Date
    {
    private:
    	int _year;
    	int _month;
    	int _day;
    public:
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	Date()
    	{}
    	void SetDate(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    };
    int main()
    {
    	Date d1, d2, d3;
    	d1.SetDate(2018, 11,10);
    	d2.SetDate(2018, 11, 11);
    	d3.SetDate(2018, 11, 12);
		Date d4(2018,11,13);
    }

如果不在类中定义析构函数,那么编译器会创造一个类似于第二个Date()的构造函数。第一个构造函数的功能类似于 SetDate() 所以构造函数的功能就是设置初始值。但是构造函数可以在定义函数的时候初始化。

构造函数的特点

1.构造函数的名称与类名一样。

2.构造函数没有返回值。

3.对象的构造函数在对象生成时自动调用,只调用一次,设置初始值。

4.构造函数可以被重载。

5.放在类里的函数编译器可能会将其作为内联函数处理

6.如果类中没有构造函数,编译器会默认创造无参的构造函数。换句话说,如果显式定义定义了构造函数,默认的构造函数将不会合成。

析构函数

析构函数的作用是在对象被销毁的是时候,调用析构函数清理对象占用的空间,整理内存。它的定义方式 ~Date() ,~类名。

	~Date()
	{
		cout << "~Date :: Called !" << endl;
	}

析构函数会在对象创建的时候,如果没有在类中显式的定义,那么系统会自动调用。虽然以上函数似乎没什么用。但是如果考虑堆上开辟空间。析构函数就非常有用了。

插入图片1

析构函数的特点

1.对象销毁的时候,由编译器自动调用

2.无参数返回值

3.一个类只能有一个析构函数

4.析构函数的调用时刻,是对象生命周期结束时。

拷贝构造函数

类似于内置类型,如果创建对象的时候需要使用别的对象来初始化,这个时候编译器会调用一个拷贝构造函数将一个已有的对象去初始化新对象。

    #include <iostream>
    using namespace std;
    class Date
    {
    private:
    	int _year;
    	int _month;
    	int _day;
    public:
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	~Date()
    	{
    		cout << "~Date :: Called !" << endl;
    	}
    	Date(Date & d1)
    	{
    		cout << "Date :: Date (Date &d1) is called !" << this << endl;
    		_year = d1._year;
    		_month = d1._month;
    		_day = d1._day;
    	}
    };
    int main()
    {
    	int a = 10;
    	int b(a);
    	Date d1(2018, 11, 16);
    	Date d2(d1);
    	system("pause");
    	return 0;
    }

以上 在 main()函数中 创建int b的时候使用了 a 中的变量进行初始化。同样的,在创建对象 Date d2 的时候使用了 d1 的成员变量进行初始化。

拷贝构造函数的特点

1.拷贝构造函数是构造函数的重载形式

2.拷贝构造函数必须是引用的形式 Date(Date &d1)。

3.如果没有吸纳是定义系统,系统会定义默认的构造函数

问题:为什么拷贝构造函数必须是引用的形式?

    	Date(Date & d1)
	  //Date(Date d1)
    	{
    		cout << "Date :: Date (Date &d1) is called !" << this << endl;
    		_year = d1._year;
    		_month = d1._month;
    		_day = d1._day;
    	}

如果是注释中的形式会造成死递归。如果在main()函数使用注释中的形式,
Date(Date d1) 那么Date d1 会再次创建一个拷贝构造函数,将主函数中的 d1 拷贝到 形参 d1中,而形参 d1 继续调用拷贝构造函数 再创建一个形参的形参d1。如此无限递归。

浅拷贝 和 深拷贝

如果编译器会默认创建拷贝构造函数,那么为什么要给用户一个接口来显式定义呢?

编译器设定的默认拷贝构造函数只是基于内存拷贝的浅拷贝。

    #include <iostream>
    using namespace std;
    
    class SeqList
    {
    private:
    	int* _PDate;
    	int  _Capacity;
    	int  _size;
    public:
    	SeqList( int size)
    	{
    		_PDate = (int *)malloc(size * sizeof(int));
    		_size = size;
    		_Capacity = _Capacity;
    	}
    	~SeqList()
    	{
    		_size = 0;
    		_Capacity = _Capacity;
    		free(_PDate);
    	}
    };
    int main()
    {
    	SeqList List1(10);
    	SeqList List2(List1);
    	system("pause");
    	return 0;
    }

以上代码在运行时会报错和中断。代码运行过程如下图:

插入图片2

此代码在执行时,首先 List2 的生命周期结束了,调用析构函数释放了 List2 的堆上空间。 然后,List1 的生命周期结束了,调用了析构函数发现
0x003498c8d地址已经被释放过了。再次释放时已经找不到 free 区间的首位标志了。所以出错了。

插入图片3

这个时候如果显示定义拷贝构造函数。那么就能够很好的处理好堆上的关系。

	SeqList(SeqList &S1)
	{
		_size = S1._size;
		_PDate = (int *)malloc(S1._size * sizeof(int));
		_Capacity = S1._Capacity;
	}

插入图片4

### C++构造函数、函数拷贝构造函数的详解 #### 1. 构造函数 构造函数是在对象被创建时自动调用的一种特殊成员函数,用于初始化对象的状态。如果没有显式定义任何构造函数,则编译器会自动生成一个默认构造函数[^1]。 - **默认构造函数** 默认构造函数是一个没有任何参数或者所有参数都有默认值的构造函数。它通常用来初始化对象到某种初始状态。需要注意的是,默认构造函数并不是完全“空”的,在某些情况下可能会执行一些必要的操作来确保对象处于有效状态[^3]。 - **全缺省构造函数** 当构造函数的所有参数都提供了默认值时,这种构造函数也可以作为默认构造函数使用。例如: ```cpp Example(int val = 0); ``` - **带参构造函数** 带参构造函数允许通过传递参数的方式初始化对象的数据成员。例如: ```cpp Example e2(10); // 调用带参数的构造函数 ``` #### 2. 函数 函数是一种特殊的成员函数,当对象生命周期结束时会被自动调用。它的主要作用是用来释放资源,比如关闭文件、释放动态分配的内存等。函数没有返回类型,也没有参数,并且其名称与类名相同,前面加波浪号 `~` 表示[^2]。 - 示例代码如下: ```cpp ~Example() { cout << "Destructor called" << endl; } ``` #### 3. 拷贝构造函数 拷贝构造函数是一种特殊的构造函数,用于从已存在的同类型对象初始化新对象。如果未显式定义拷贝构造函数,编译器会提供一个默认版本,该版本逐位复制数据成员。然而,对于涉及指针或其他复杂资源管理的情况,这可能导致问题,因此常常需要自己定义拷贝构造函数[^3]。 - 示例代码如下: ```cpp Example(const Example& other) : value(other.value) { cout << "Copy constructor called" << endl; } ``` #### 关键区别总结 | 特性 | 构造函数 | 函数 | 拷贝构造函数 | |---------------------|-----------------------------------|------------------------------|-------------------------------| | 自动调用时机 | 创建对象时 | 销毁对象时 | 使用已有对象初始化新对象时 | | 参数 | 可有可无 | 无 | 接收常量引用类型的现有对象 | | 主要用途 | 初始化对象 | 清理资源 | 复制对象 | ```cpp #include <iostream> using namespace std; class Example { public: // 默认构造函数 Example() { cout << "Default constructor called" << endl; } // 带参构造函数 Example(int val) : value(val) { cout << "Parameterized constructor called with value: " << val << endl; } // 拷贝构造函数 Example(const Example& other) : value(other.value) { cout << "Copy constructor called" << endl; } // 函数 ~Example() { cout << "Destructor called" << endl; } void print() const { cout << "Value: " << value << endl; } private: int value; }; int main() { Example obj1; // 调用默认构造函数 Example obj2(42); // 调用带参构造函数 Example obj3(obj2); // 调用拷贝构造函数 obj1.print(); obj2.print(); obj3.print(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值