拷贝构造函数和拷贝赋值运算符
1. 普通情况下,拷贝构造和拷贝赋值运算符的书写
- 注意
在默认情况下(用户没有定义,但是也没有显示的删除),编译器会自动隐式生成一个拷贝构造函数和赋值运算符,但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算。
需要注意的是,拷贝构造函数必须以引用的方式传递参数,这是因为,在值传递给一个函数的时候,会调用拷贝构造函数生成函数的实参,如果拷贝构造函数的参数仍然是以值的方式,就会无限循环的调用下去,直到函数的栈溢出。
void func(alpha); //以值传递对象
func(a1); //函数调用
这时a1的拷贝构造函数将会被调用来创建一个对象a1的副本,并将副本交给函数func()操作。(当然引用或者指针就不会调用拷贝构造函数)
-
区别
拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象,但是其结果却有些不同,拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。这种区别从两者的名字也能轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给另一个对象(已经存在的)。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生,如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。 -
延伸:this指针
每一个对象的成员函数都可以访问一种神奇的指针,即指向该对象本身的this指针,因而在任何对象中都可找到所属对象的自身地址。拷贝赋值构造函数中使用this指针作为返回值,指的就是被赋值的对象本身。
需要注意,this指针在静态成员函数中是无效的,因为静态成员函数不属于任何特定的对象。
namespace _nmsp1
{
class A {
A(): m_caa(0), m_cab(0), m_cap(new char[100]) {} // 构造函数
// A(const A& tmpobj) = delete; //可以删除默认的拷贝构造函数
~A()
{
delete [] m_cap;
}
A(const A& tmpobj) //拷贝构造函数
{
// 拷贝构造函数有新的对象实例产生。m_cap首先分配内存,然后将要拷贝的值赋给m_cap
m_cap = new char[100];
memcpy(m_cap, tmpobj.m_cap, 100);
m_caa = tmpobj.caa;
m_cab = tmpobj.cab;
}
// A& operator= (const A& tmpobj) = delete; //可以删除默认的拷贝赋值运算符
A& operator= (const A& tmpobj) //赋值构造函数
{
// 防止自我赋值
if(this == &tmpobj)
{
return *this;
}
// 复制构造函数没有新的对象产生,原来已经有对象了,所以需要先删除原来的动态数组。
delete [] m_cap; // 如果只调用delete m_cap,那么只会删除数组的第一个数据
m_cap = new char[100];
memcpy(m_cap, tmpobj.m_cap, 100);
m_caa = tmpobj.caa;
m_cab = tmpobj.cab;
return *this; // 指向该成员函数所属的对象
}
}
public:
int m_caa;
int m_cab;
char* m_cap; //指向定长100的字符;
}
int main()
{
_nmsp1::A aobj1;
aobj1.m_caa = 10; aobj1.m_cab = 20;
_nmsp1::A aobj2(aobj1); //调用拷贝构造函数
_nmsp1::A aobj4 = aobj1; // 调用拷贝构造函数,因为有新的类对象实例产生
_nmsp1::A aobj3;
aobj3 = aobj1; //调用拷贝赋值构造法
}
2. 继承关系下,拷贝赋值运算符和拷贝构造函数的书写
如果在子类中写了拷贝构造函数和拷贝赋值运算符,那么对于父类中的拷贝构造函数和拷贝赋值运算符,需要程序员自己调用,否则会调用子类自己的拷贝构造函数和拷贝赋值运算符。
class C : public A
{
public:
C() {};
C(const C& tmpobj):A(tmpobj) // 拷贝构造函数,调用父类的拷贝构造函数
{
}
C& operator= (const C& tmpobj) // 拷贝赋值运算符
{
A::operator= (tmpobj); // 调用父类的拷贝赋值运算符
return *this
}
}