C++ 编程技巧与规范(二)
2020-11-19 本文已影响0人
e196efe3d7df
拷贝构造函数和拷贝复制运算符的书写
如下:
namespace demo2 {
class A {
public:
A():m_i(0), m_j(0) {}
A(int i, int j):m_i(i), m_j(j) {}
A(const A& temp) {
m_i = temp.m_i;
m_j = temp.m_j;
}
A& operator=(const A& temp)
{
m_i = temp.m_i;
m_j = temp.m_j;
return *this;
}
public:
int m_i;
int m_j;
};
}
int main()
{
demo2::A a1;
demo2::A a2 = a1; //拷贝构造
demo2::A a3;
a3 = a1; //拷贝赋值
}
对象自我赋值产生的问题
对象的自我赋值,会产生一些问题,比如:
#pragma warning(disable:4996) //strcpy会触发4996警告,头部加入 #pragma warning(disable:4996) 即可
namespace demo3 {
class A {
public:
A() :m_i(0), m_j(0), m_charPtr(new char[100]){}
A(int i, int j) :m_i(i), m_j(j), m_charPtr(new char[100]) {}
A(const A& temp) {
m_charPtr = new char[100];
memcpy(m_charPtr, temp.m_charPtr, 100);
m_i = temp.m_i;
m_j = temp.m_j;
}
A& operator=(const A& temp)
{
delete m_charPtr;
m_charPtr = new char[100];
memcpy(m_charPtr, temp.m_charPtr, 100);
m_i = temp.m_i;
m_j = temp.m_j;
return *this;
}
public:
int m_i;
int m_j;
char* m_charPtr;
};
}
int main()
{
demo3::A a;
strcpy(a.m_charPtr, "abcdef"); //strcpy会触发4996警告,头部加入 #pragma warning(disable:4996) 即可
a = a;
}
A的赋值运算符重载函数,会先清理自身的m_charPtr,然后重新分配内存,把目标的m_charPtr赋值给自身的m_charPtr。当自我赋值时,会目标对象和自身对象是同一块内存,所以清理后,数据丢失,无法进行复制。可以进行如下改动
- 如果是同一对象的自我赋值,则直接返回自身。
A& operator=(const A& temp)
{
if (this == &temp)
{
return *this;
}
delete m_charPtr;
m_charPtr = new char[100];
memcpy(m_charPtr, temp.m_charPtr, 100);
m_i = temp.m_i;
m_j = temp.m_j;
return *this;
}
- 先把目标对象的资源复制一份,然后用复制的资源进行赋值。
A& operator=(const A& temp)
{
char* tempCharPtr = new char[100];
memcpy(tempCharPtr, temp.m_charPtr, 100);
delete m_charPtr;
m_charPtr = tempCharPtr;
m_i = temp.m_i;
m_j = temp.m_j;
return *this;
}
第一种,简单明了。第二种,效率可能会更高
继承关系下拷贝构造函数和拷贝复制运算符的书写
如果在子类中没有定义自己的拷贝构造函数和拷贝复制运算符函数,则编译器会调用父类的拷贝构造函数和拷贝复制运算符函数。
反之,如果子类中定义自己的拷贝构造函数和拷贝复制运算符函数,则编译器会调用子类字自身的拷贝构造函数和拷贝复制运算符函数,不再调用父类的拷贝构造函数和拷贝复制运算符函数,需要程序员自己去调用父类的拷贝构造函数和拷贝复制运算符函数。如下:
namespace demo4 {
class A {
public:
A() :m_i(0), m_j(0), m_charPtr(new char[100]) {}
A(int i, int j) :m_i(i), m_j(j), m_charPtr(new char[100]) {}
A(const A& temp) {
m_charPtr = new char[100];
memcpy(m_charPtr, temp.m_charPtr, 100);
m_i = temp.m_i;
m_j = temp.m_j;
}
A& operator=(const A& temp)
{
char* tempCharPtr = new char[100];
memcpy(tempCharPtr, temp.m_charPtr, 100);
delete m_charPtr;
m_charPtr = tempCharPtr;
m_i = temp.m_i;
m_j = temp.m_j;
return *this;
}
virtual ~A() {}
public:
int m_i;
int m_j;
char* m_charPtr;
};
class B :public A {
public:
B() {}
~B(){}
B(const B& temp) :A(temp) {
//A(temp) 不能将调用父类复制构造函数放在此处,会造成二义性,编译错误,编译器会解析成 A temp;
}
B& operator=(const B& temp) {
A::operator=(temp);
return *this;
}
};
}
int main()
{
demo4::B b1;
demo4::B b2 = b1; //拷贝构造函数
b2 = b1; //拷贝赋值运算
}
拷贝构造函数和拷贝复制运算符中重复代码的处理
不建议拷贝构造函数和拷贝复制运算符函数互相调用