假设定义了一个ClassDemo的类
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num);
~ClassDemo();
private:
int _num;
};
在main函数中
CLassDemo demo = 10;
demo = 20;
第一句话代表的并不是赋值,此时会调用转换构造函数。
第二句话会调用默认的赋值函数:先通过转换构造函数创建一个临时的对象,再将该对象传递给默认的赋值函数,最后析构该临时对象。
请参考构造函数讲解
如果在main函数中
ClassDemo demo = 10;
ClassDemo demo1 = demo;
第二句话并非一个赋值,此时它会调用复制构造函数。
这是复制构造函数的函数原型,它会将类中的所有成员变量复制到对象中
ClassDemo(const ClassDemo &other);
复制构造函数的形参必须是一个引用,否则编译是无法通过的。如果形参不是一个引用的话,此时会产生无限递归,因为将一个对象传递到复制构造函数中时,此时会为该对象产生一个副本,而产生副本的过程就会调用复制构造函数,此时便产生了无限递归。
举个例子:
void f1(ClassDemo demo)
{
}
void f2(ClassDemo &demo)
{
}
在main函数调用这两个函数产生的结果是不一样的
ClassDemo demo;
f1(demo);
f2(demo);
程序在执行到f1的时候,因为f1的形参为类的对象。此时首先会调用复制构造函数创建这个对象。然后才会进入f1函数执行函数体的部分。在f1执行完之后该对象会被析构。
程序执行到f2的时候,因为传递的是一个对象的引用,此时不会有新的对象产生,复制构造函数不会执行,此时会直接执行f2函数体部分,也不会有析构函数的调用。
学到这里可以知道,在一个空类中有4个函数会被默认生成。
默认构造函数
默认析构函数
默认复制构造函数
默认赋值函
关于复制构造函数的使用:
class MyString
{
pubilc:
MyString(char *str):_len(strlen(str))
{
new char[_len + sizeof(char)];
strcpy(_str, str);
}
~MyString()
{
if(_str)
delete[]_str;
}
MyString(MyString &other)
{
_str = other._str;
}
MyString& operator=(MySting &other)
{
_len = other._len;
_str = other._str;
return *this;
}
char *GetString()
{
return _str;
}
private:
char *_str;
};
int main()
{
MyString demo = "I Have Apple";
MyString demo1 = demo;
std::cout << demo1.GetString();
demo1 = "I Have Pen";
std::cout << demo1.GetStrinf();
return 0;
}
打印的结果为:
这是因为在执行demo1 = “I Have Pen”的时候会调用默认的赋值函数。
默认的赋值函数首先会创建一个临时的对象temp
然后会将该临时对象的值给demo1
即为:
MyString operator=(const MyString& temp)
{
_str = temp._str;
_len = temp._len;
}
在该赋值函数结束后,临时对象temp会被析构掉。所以指向temp._str的空间会被delete。此时的demo1. _str所指向的空间被delete了,demo1. _str也就变成了野指针,所以打印出来的结果自然也是未知的。在vs编译器的dubug下这段空间会被填充成0xdddddddd。
所以当我们类的成员变量中存在指针的时候,使用默认的复制构造函数和默认的赋值函数时就会存在风险
这种直接对指针的赋值称为浅拷贝。使得指针易被delete,在这种情况下应当使用深拷贝来维护该成员变量的生命周期。
MyString(MyString &other)
{
delete[]_str;
_len = other._len;
_str = new char[_len + sizeof(char)];
strcpy(_str, other._str);
}
MyString& operator=(MySting &other)
{
delete[]_str;
_len = other._len;
_str = new char[_len + sizeof(char)];
strcpy(_str, other._str);
return *this;
}
深拷贝实质上是维护了对象中所有属性的生命周期。使其生命周期和对象是同步的。