#include <stdio.h>
#include <iostream>
using namespace std;
class String
{
private:
char *str;
public:
String(char *p = NULL)
{
if (p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str,p);
}
else
{
str = NULL;
}
}
~String()
{
delete []str;
}
void print() const
{
if (str != NULL)
{
printf("%s\n",str);
}
}
};
int main()
{
char ch1[] = "hello world";
String s1(ch1);
s1.print();
return 0;
}
以上,是一个普通的String类,编译运行也不会出现任何错误。
但是,当在main()中再构造一个S2,用S1调动系统的拷贝构造函数构造S2时,则出现崩溃。
int main()
{
char ch1[] = "hello world";
String s1(ch1);
String s2(s1);
s1.print();
s2.print();
return 0;
}

这是为什么呢~~~
首先,我们都知道,当拿一个对象去初始化另一个对象的时候,如果没有自定义的拷贝构造函数,则要调用系统的缺省拷贝构造函数,在String类中,缺省的拷贝构造函数如下
String(const String &s):s(s.str)
{}
系统缺省的拷贝构造函数是怎么做到拷贝构造的呢?
待图解:

于是,在main()函数开始后,首先调动构造函数开辟空间构造s1,并且用ch1字符串为其初始化。假如,这片空间的首地址是:0x0065fa82
那么,当函数构造s2对象的时候,则需要调用系统默认的拷贝构造函数去构造s2,可是,s2的str空间却因此与s1相同了有木有!!!任何一个对象的str改变都会造成另一个的
str也改变的好不好!!!
然后,main()结束,调用析构函数析构掉s1、s2.此时,出问题了,因为两个对象要析构两次,也就是说,要释放两次str空间,可是,明明两个对象的str都指向了同一片空间,这样,就意味着~~~~~~你懂得。于是,就崩溃了。
这就是传说中的,浅拷贝。
可以设想一下,假如系统默认的拷贝构造都是这么简单的话,那么当你所定义的任何一个类中,它的数据成员中有指针存在时,你是否会担心发生相同的问题呢。这是很严肃很现实的。
怎么解决?
答案就是:自己定义拷贝构造函数。
当拿一个对象去初始化另一个对象的时候,老老实实的根据所拿对象的指针空间大小来开辟相同大小的空间,是字符串就strcpy()过去,是int等等就挨个赋值过去。
记着,字符串末尾的'\0'在内存中是要占空间的,但是,strlen()出来的大小,却不包括字符串末尾的'\0',so~~~~开辟空间的时候,你得自己多开一个。
说的我口干,喝口水去,贴上源码,自己看吧。
class String
{
private:
char *str;
public:
String(char *p = NULL)
{
if(p != NULL)
{
str = new char[strlen(p) + 1];
strcpy(str,p);
}
else
{
str = new char[1];
*str = '\0';
}
}
String(const String &s)
{
if(str != s.str)
{
str = new char[strlen(s.str) + 1];
strcpy(str,s.str);
}
}
~String()
{
delete []str;
str = NULL;
}
void Print() const
{
cout<<str<<endl;
}
};