C++语言构造函数与析构函数需要注意的地方。
构造
考虑以下定义
struct Node { char *name; int value; Node(char *name = "", int value = 0) { name = new char[strlen(name)+1]; strcpy(name, name); value = value; } }
声明两个对象
Node node1("child", 10), node2(node1);
这行语句创建了两个对象node1,node2,其中node2是用node1的成员进行初始化,现在改变node2成员值
strcpy(node2.name, "parent"); node2.value = 40;
打印对象的成员值如下
cout<<node1.name<<' '<<node1.value<<' '<<node2.name<<' '<<node2.value;
结果如下
parent 10 parent 40
问题出在Node结构成员name是一个指针类型,如果结构定义中没有复制构造函数,则编译器使用默认的构造函数,此构造函数在复制值类型与复制指针类型时实现方法不同,复制指针类型时仅仅复制指针的内容(所指向的地址),所以node1和node2的name指针指向了同一个地址。
解决这个问题的办法是自定义一个复制构造函数
struct Node { char *name; int value; Node(char *name = "", int value = 0) { name = new char[strlen(name)+1]; strcpy(name, name); value = value; } Node(const Node& node) { name = new char[strlen(node.name) + 1]; strcpy(name, node.name); value = node.value; }
当然,还有一个类似的问题就是
node2 = node1;
这句代码也是对成员进行浅表复制,解决的办法就是自定义赋值构造函数,仅给出代码片段
Node& operator=(const Node& node) { if(this != &n) { // no assignment to itself if(name != 0) delete [] name; // 注意这里是 delete [],delete只是析构单个对象,而非字符数组 name = new char[strlen(node.name) + 1]; strcpy(name, node.name); value = node.value; } return *this; }
析构
对于以上类型的对象,因为是值类型,所以如果是作为局部变量,当对象处于作用范围之外时将被销毁,被对象占用的内存也会得到释放。然而,这个对象占用的内存虽然被释放,但是跟这个对象相关联的内存则不一定被释放。如上面这个Node类型,有一个指向string的指针成员,因此,销毁Node类型对象时,对象的指针成员(name)所占用的内存被释放(32位机器上指针占用4byte),但是指针指向的string所占用的内存将会变得无法访问,这个内存也就无法被释放了。为了避免这个问题,类(结构)定义中需要包含析构函数。析构函数在对象被销毁时自动被调用,或者主动使用delete关键字调用析构函数。析构函数不接受参数,也不返回值。
在Node的定义增加一个析构函数
~Node() { if(name != 0) delete [] name; }
以上讨论了结构(struct)的构造函数和析构函数,对于类(class)的情况类似。