文章目录
本文主要搞清楚以下两种写法区别:(看不懂的话可以把
*p_m1
换成
m1
)
- 拷贝构造函数
MyClass m2(*p_m1); // 或:MyClass m2 = *p_m1;
- 等号=赋值运算符重载函数
MyClass m2;
m2 = *p_m1;
先看一段代码:
#include <iostream>
#include <cstdlib>
#include <limits>
#include <string>
using namespace std;
class MyClass
{
public:
int *ptr;
MyClass()
{
ptr = new int[10];
}
~MyClass()
{
delete[] ptr;
}
MyClass(const MyClass &other) // 拷贝构造函数 //写了这个不用写重载等号代码,调用MyClass m1 = m2;相当于调用了拷贝构造函数
{
ptr = new int[10];
for (int i = 0; i < 10; i++)
{
ptr[i] = other.ptr[i];
}
}
};
int main()
{
int *p_int;
// MyClass m1;
MyClass *p_m1 = new MyClass(); // 创建对象
p_int = p_m1->ptr;
for (auto i = 0; i < 10; i++)
{
p_m1->ptr[i] = i;
}
*p_int = 111;
for (auto i = 0; i < 10; i++)
{
cout << p_m1->ptr[i] << endl;
}
MyClass m2 = *p_m1; //没问题 //这个调用了我们上面实现的拷贝构造函数,我把拷贝构造函数注释掉之后就不行了(变成浅拷贝)
// MyClass m2; // 段错误 //free(): double free detected in tcache 2 //Aborted (core dumped) /浅拷贝
// m2 = *p_m1;
// MyClass m2(*p_m1); //没问题 //调用了上面我们实现的拷贝构造函数
delete (p_m1);
*p_int = 888;
for (auto i = 0; i < 10; i++)
{
cout << m2.ptr[i] << endl;
}
std::cout << "Press ENTER to continue...";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 读取内容直到用户输入enter键
return EXIT_SUCCESS;
}
我用:
MyClass m2 = *p_m1;
能自动调用我们实现的拷贝构造函数,但是:
MyClass m2;
m2 = *p_m1;
却不行?
原因:
这是因为
MyClass m2 = *p_m1;
这种写法是定义并初始化一个新的对象
m2
,在定义时就会调用拷贝构造函数来初始化它。而MyClass m2; m2 = *p_m1;
这种写法是先定义了一个对象
m2
,然后再将*p_m1
赋值给它,这里的赋值操作会调用赋值运算符重载函数,而不是拷贝构造函数。 如果想要使用m2 = *p_m1;
这种写法来调用拷贝构造函数,需要在类中实现赋值运算符重载函数。
另外一点需要注意:
MyClass m2 = *p_m1;
与
MyClass m2(*p_m1);
在用法上是等价的,可以理解为当我们调用MyClass m2 = *p_m1;
时,相当于调用了MyClass m2(*p_m1);
如果我们想要使用MyClass m2; m2 = *p_m1;
这种写法,只需要把等号=赋值运算符重载函数实现一下就好了:
MyClass &operator=(const MyClass &other)
{
if (this->ptr != NULL)
{
delete[] this->ptr;
}
this->ptr = new int[10];
for (int i = 0; i < 10; i++)
{
this->ptr[i] = other.ptr[i];
}
return *this;
}
但是同样,如果只实现了“等号=赋值运算符重载函数”,而“拷贝构造函数”未实现的话,那么 MyClass m2 = *p_m1;
和MyClass m2(*p_m1);
的写法也都是不行了(默认浅拷贝)