

1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <error.h> 6 #include <iostream> 7 #include <string> 8 using namespace std; 9 class A{ 10 int i; 11 }; 12 class B{ 13 A *p; 14 public: 15 B(){p=new A;} 16 ~B(){delete p;} 17 /* 18 B(const B& ths){ 19 p = ths.p; 20 }*/ 21 }; 22 void sayHello(B x){ 23 } 24 int main(){ 25 B b; 26 sayHello(b); 27 }
这里的错误原因是编译器在生成default copy construction的时候使用的bitwise copy语义,也就是只是简单的浅拷贝。 上面被注释掉的程序就是编译器自动添加的部分。 从而导致在sayHello中向参数x传递值时,调用了bitwise copy的拷贝构造函数,使得x对象和b对象中的值完全一致,包括p指针的值,在x离开作用域(也就是sayHello函数结束),x发生析构,调用delete 销毁了指针p,同时在main函数结束的时候,析构b时又会调用一次delete删除指针p。
也就是本程序会delete一直已经被delete 的指针。可以做如下改进,来修复程序:


1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <error.h> 6 #include <iostream> 7 #include <string> 8 using namespace std; 9 class A{ 10 int i; 11 }; 12 class B{ 13 A *p; 14 public: 15 B(){p=new A;} 16 ~B(){delete p;} 17 B(const B& other){ 18 p = new A; //构建新的指针 19 *p = *(other.p); //将指向的内容复制,依然指向不同的位置 20 } 21 }; 22 void sayHello(B b){ 23 } 24 int main(){ 25 B b; 26 sayHello(b); 27 }
注释:
以下三种情况需要用到拷贝函数:
a)一个对象以值传递的方式传入函数体;
b)一个对象以值传递的方式从函数返回;
c)一个对象需要通过另外一个对象进行初始化。
作为实参传递给非引用类型的形参,会调用拷贝构造函数,默认的是浅拷贝,涉及到指针时会有大问题


1 #include <unistd.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 #include <fcntl.h> 5 #include <error.h> 6 #include <iostream> 7 #include <string> 8 using namespace std; 9 class A{ 10 int i; 11 }; 12 class B{ 13 A *p; 14 public: 15 B(){p=new A;} 16 ~B(){delete p;} 17 /* 18 B(const B& ths){ 19 p = ths.p; 20 }*/ 21 }; 22 void sayHello(B &x){//引用 23 } 24 int main(){ 25 B b; 26 sayHello(b); 27 }
主要考察“浅拷贝”和“深拷贝”。
大多情况下“浅拷贝”可以满足需求 ,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,为了防止编译器默认浅拷贝的发生,可以声明一个私有的拷贝构造函数, 甚至不必去定义这个拷贝构造函数,这样因 为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从 而可以避免按值传递或返回对象。