C++编译器会为每个类自动生成一个默认的构造函数、析构函数、赋值函数、拷贝构造函数,这当然是在你没有为你的类声明这些函数的时候。这些默认的功能函数在为你提供方便的时候,也会给你带来麻烦。 例如: class string { public: string(const char *value); ~string(); ... // 没有拷贝构造函数和operator= private: char *data; }; string::string(const char *value) { if (value) { data = new char[strlen(value) + 1]; strcpy(data, value); } else { data = new char[1]; *data = '\0'; } } inline string::~string() { delete [] data; } //注意:new 和delete 要采用相同的形式。 如果有string的两个对象, string a("hello"); string b("world"); 当b=a时,因为你自己没为类定义那些函数,所以C++编译器会提供默认的赋值函数,这个缺省的赋值操作符会执行从a的成员到b的成员的逐个成员的赋值操作,对指针(a.data和b.data) 来说就是逐位拷贝。这种情况下至少有两个问题。 第一,b曾指向的内存永远不会被删除,因而会永远丢失。这是产生内存泄漏的典型例子。 第二,现在a和b包含的指针指向同一个字符串,那么只要其中一个离开了它的生存空间,其析构函数就会删除掉另一个指针还指向的那块内存,重复析构的问题。 下面的语句: string a("hello"); // 定义并构造 a { // 开一个新的生存空间 string b("world"); // 定义并构造 b ... b = a; // 执行 operator=, 调用默认赋值函数 // 丢失b的内存,造成内存泄露。 } // 离开生存空间, 调用 // b的析构函数 string c = a; // c.data 的值不能确定! 调用默认的拷贝构造函数 // 但是a.data 已被删除,无法进行拷贝构造。 当这类对象进行函数参数按值传递时,形参会按照缺省的拷贝构造函数进行初始化,形参拥有一个指向该对象指针的一个拷贝,当函数结束时,形参会调用析构函数,该对象的指针也被销毁。 class stack { public: stack(const char *value); ~stack(); char * data; }; stack::stack(const char *value) { if(value) { data= new char[strlen(value)+1]; strcpy(data,value); } else { data= new char[1]; *data = '\0'; } } inline stack::~stack() { delete []data; } void dosth(stack pstk) { cout<<pstk.data<<endl; } int main() { stack str("iamxczhang"); dosth(str); } 在析构函数出设立断点,你会看到析构函数执行两次! 牢记:只要类里有指针变量就得自己写拷贝构造函数和赋值函数,但是你确定用不着这些函数时,可以把这些函数做private声明而不去实现它,这就防止了会有人去调用它们,也防止了编译器去生成它们。
vinson