前言
本文承接上文,主要介绍C++语法中的另一个极其重要的成员函数——拷贝构造函数
简要介绍
如果一个构造函数的第一个参数是自身类型的引用,且其余参数均有默认值,那么这个构造函数就是拷贝构造函数,拷贝构造的效果如下
int main()
{
Date d1(2026, 2, 21);
d1.Print();
Func1(d1);
Date d2(d1);
d2.Print();
return 0;
}
也就是能用已经初始化的d1初始化d2,这就是拷贝构造的效果
特点
- 拷贝构造是构造函数重载
- C++规定传值传参必须调用拷贝构造
- 拷贝构造的第一个参数必须是类类型对象的引用 如果第一个参数不是引用,只是一个类类型对象,那么就会发生传值传参,调用拷贝构造,然后又会拷贝,因此必须是传引用传参,这样的话问题就迎刃而解了。
- 如果不希望传进去的实参发生改变,那么可以加一个const从而保护实参
- 编译器自动生成的拷贝构造函数会对内置类型进行浅拷贝(按字节一个一个拷贝),对于自定义成员会调用其拷贝构造
- 如果一个类显示实现了析构函数并释放了资源,那么就必须显示写拷贝构造
typedef int STDataType;
class Stack
{
public:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
// st2(st1)
Stack(const Stack& st)
{
cout << "Stack(const Stack& st)" << endl;
// 需要对_a指向资源创建同样大的资源再拷贝值
_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
if (nullptr == _a)
{
perror("malloc申请空间失败!!!");
return;
}
memcpy(_a, st._a, sizeof(STDataType) * st._top);
_top = st._top;
_capacity = st._capacity;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
~Stack()
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
比如栈这个结构,如果不显示写拷贝构造,那么按照字节拷贝会将st1和st2指向同一块空间,析构的时候会对一块空间析构两次,造成程序崩溃,因此需要自己写。
- 注意一个易错点:传值返回会产生一个临时对象从而调用拷贝构造,因此应该传引用返回来减少拷贝构造的调用,返回的是返回对象的引用,但是这是如果返回的是当前函数局部域的局部对象,函数结束后对象销毁,这时引用就变成一个野引用了,因此需要保证返回对象的生命周期,当然加一个static也可以
Stack& func2()
{
static Stack st;//不加static的话出了作用域st就不在了
return st;
}
int main()
{
Stack ret = func2();
return 0;
}
结语
拷贝构造函数到此就介绍完了,希望读者关注下文