拷贝构造函数

目录

一:概念

二:特征


一:概念

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时,由编译器自动调用。即:用同类类型对象拷贝初始化


二:特征

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

2.拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。(传值传参时要调用拷贝构造函数)

class Date
{
public:
	//构造
	Date(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//传值传参时,传自定义类型会调用拷贝构造
	//如果不想调用拷贝构造,两种解决方法
	//传地址(此时并不是拷贝构造,只是构造函数的一种重载形式)
	Date(Date* d)
	{
		_year = d->_year;
		_month = d->_month;
		_day = d->_day;
	}

	//二:传引用(拷贝构造)
	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1;//构造
	Date d2(&d1);//也是构造

	Date d3(d1);//拷贝构造
	Date d4 = d1;//拷贝构造的另一种写法

	return 0;
}

用const修饰拷贝构造参数,是为了出现以下情况 

//这样写编译器不会报错,但是顺序写反了,不仅不会完成拷贝,还会把原来的值改变了   
 Date(Date& d)
	{
		d._day = _day;
		d._month = _month;
		d._year = _year;
	}

如果用来const修饰,上面的情况就会报错

3.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
class Time
{
public:
     Time()
     {
         _hour = 1;
         _minute = 1;
         _second = 1;
     }
     Time(const Time& t)
     {
         _hour = t._hour;
         _minute = t._minute;
         _second = t._second;
         cout << "Time::Time(const Time&)" << endl;
     }
private:
     int _hour;
     int _minute;
     int _second;
};
    
class Date
{
private:
     // 基本类型(内置类型)
     int _year = 1970;
     int _month = 1;
     int _day = 1;
     // 自定义类型
     Time _t;
};
int main()
{
     Date d1;
    
    // 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
    // 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构
造函数
     Date d2(d1);
     return 0;
}
注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定
义类型是调用其拷贝构造函数完成拷贝的。
4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了 ,还需要自己显式实现吗?
当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
typedef int DataType;

class Stack
{
public:
      Stack(size_t capacity = 10)
     {
         _array = (DataType*)malloc(capacity * sizeof(DataType));
         if (nullptr == _array)
             {
                 perror("malloc申请空间失败");
                 return;
             }
         _size = 0;
         _capacity = capacity;
     }
     void Push(const DataType& data)
     {
         // CheckCapacity();
         _array[_size] = data;
         _size++;
     }
     ~Stack()
     {
         if (_array)
         {
             free(_array);
             _array = nullptr;
             _capacity = 0;
             _size = 0;
          }
 }
private:
     DataType *_array;
     size_t _size;
     size_t _capacity;
};

int main()
{
     Stack s1;
     s1.Push(1);
     s1.Push(2);
     s1.Push(3);
     s1.Push(4);
     Stack s2(s1);
     return 0;
}
由此可以看出如果是栈,不写显示拷贝构造,程序会报错
但是通过调试,我们又看到st2是拷贝成功的,那为什么最后会报错呢?

所以,类中如果没有涉及资源的申请时,拷贝是否写都可以;但是一旦涉及自愿申请时,则拷贝构造是一定要写,否则就是浅拷贝。

浅拷贝:按字节一个一个的拷贝

深拷贝:开一样大的空间,形状,空间均复制一样

typedef int SLType;

class Stack
{
public:
	Stack(int n = 4)
	{
		SLType* tmp = (SLType*)malloc(sizeof(SLType) * n);
		if (tmp == nullptr)
		{
			perror("malloc fail");
			return;
		}

		_a = tmp;
		_capacity = n;
		_size = 0;
	}
	void Push(SLType x)
	{
		_a[_size++] = x;
	}

	//深拷贝
	Stack(const Stack& st)
	{
		_a = (SLType*)malloc(sizeof(SLType) * st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			return ;
		}
		_capacity = st._capacity;
		_size = st._size;

		memcpy(_a, st._a, sizeof(SLType) * st._size);
	}

	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_size = _capacity = 0;
		}
	}
private:
	SLType* _a;
	int _size;
	int _capacity;
};

int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);

	Stack st2 = st1;

	return 0;
}

 5.拷贝构造函数典型调用场景

使用已经存在的对象创建新对象

函数参数类型为类类型

函数返回值类型为类类型

class Date
{
public:
	Date(int year, int month, int day)
	{
		cout << "Date(int,int,int)" << this << endl;
	}

	Date(const Date& d)
	{
		cout << "Date(const Date& d):" << this << endl;
	}

	~Date()
	{
		cout << " ~Date()" << this << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

Date Test(Date d)
{
	Date temp(d);

	return temp;
}

int main()
{
	Date d1(2024, 4, 23);

	Test(d1);

	return 0;
}

 为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用


三:总结 

1.如果没有管理资源,一般情况下不用写拷贝构造,默认生成的拷贝构造就可以,例如:Date

2.如果都是自定义类型,内置类型没有指向资源,也类似默认生成的拷贝构造,如MyQueue

3.一般情况下,不需要写析构函数就不需要写拷贝构造

4.如果内部有指针或一些指向资源,需要写析构函数,通常就需要写构造完成深拷贝,如:Stack List  二叉树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玖剹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值