在上一课,复制构造函数重载赋值运算符,您了解和复制构造函数和赋值操作符不同之处。这一课是后续的那一个。
浅拷贝
由于C++不知道很多关于你的类,默认的复制构造函数,赋值操作符可以使用复制的方法被称为浅表副本和默认(也称为逐个成员的副本)。浅拷贝意味着C + +份班级的每个成员单独使用赋值运算符。当类是简单的(如不包含任何动态分配的内存),这个作品非常好。
例如,让我们看一看仙班:
当C++做的这类浅表副本,将副本m_ncents使用标准的整数赋值操作符。因为这正是我们想要做的事,如果我们写自己的复制构造函数或赋值操作符重载,实在没有理由要写自己的这些功能的版本!
然而,在设计类的时候,处理动态分配的内存,逐个成员(浅)复制可以给我们很多麻烦!这是因为标准指针赋值运算符只是拷贝的指针地址不分配任何内存或复制的内容是指!
让我们在这方面的一个例子看看:
类mystring
{
私人的:
char * m_pchstring;
国际m_nlength;
公共:
建立(char * pchstring =“”)
{
/ /查找字符串的长度
/ /加终止一个字符
m_nlength = strlen(pchstring)+ 1;
/ /分配一个缓冲区的长度等于这
m_pchstring =新字符[ m_nlength ];
/ /复制参数为我们的内部缓冲区
strncpy(m_pchstring,pchstring,m_nlength);
//确保字符串
m_pchstring [ m_nlength-1 ] =“0”;
}
~ mystring() /析构函数
{
/我们需要释放我们的缓冲区
删除m_pchstring [ ];
//设置m_pchstring空以防万一
m_pchstring = 0;
}
char * getstring() { return m_pchstring;}
getlength() { int返回m_nlength;}
};
以上是一个简单的字符串类,分配内存以保存字符串,我们通过在。注意我们没有定义复制构造函数或赋值操作符重载。因此,C + +提供了一个默认的复制构造函数和赋值操作符,默认的浅表副本。
现在,考虑下面的代码片段:
建立几个(“你好,世界!”);
{
建立ccopy =智;/ /使用默认的复制构造函数
} // ccopy超出范围
std::cout <<智的。getstring() << std::endl;//这会崩溃
这段代码看起来是无害的,它包含了一个阴险的问题,将导致程序崩溃!你能看到它吗?如果你不能不担心,这是相当微妙的。
我们把这个例子行:
1
建立几个(“你好,世界!”);
这条线是无害的。这叫mystring构造函数,分配一些内存,集chello.m_pchstring指向它,然后复制字符串“你好,世界!“进去。
1
建立ccopy =智;/ /使用默认的复制构造函数
这似乎是无害的足够好,但它实际上是我们问题的根源!当这条线进行评价,C++将使用默认的拷贝构造函数(因为我们没有提供自己的),哪一个浅拷贝chello.m_pchstring指针。因为浅指针复制仅复制指针的地址,chello.m_pchstring的地址复制到ccopy.m_pchstring。作为一个结果,ccopy.m_pchstring和chello.m_pchstring都指向相同的内存块!
1
} // ccopy超出范围
当ccopy超出范围,例如下面的调用析构函数,在ccopy。析构函数删除动态分配的内存,ccopy.m_pchstring和chello.m_pchstring指向!因此,通过删除ccopy,我们也知道(无意中)的影响。请注意,析构函数将ccopy.m_pchstring至0,但chello.m_pchstring将指向被删除的(无效的)记忆!
1
std::cout <<智的。getstring() << std::endl;//这会崩溃
现在你可以看到为什么这崩溃。我们删除了几个指向的字符串,现在我们想打印的内存值,不再分配。
这个问题的根源是由复制构造函数做了浅拷贝做浅拷贝的复制构造函数中的指针值或重载的赋值操作符总是自找麻烦。
深拷贝
这个问题的答案是任何非空指针被复制做深拷贝。