zoukankan      html  css  js  c++  java
  • c++ 拷贝构造函数(重点在内含指针的浅拷贝和深拷贝)

    今天同事问了一个关于拷贝构造函数的问题,类中包含指针的情况,今天就来说说c++的拷贝构造函数。

    c++的拷贝构造函数是构造函数的一种,是对类对象的初始化,拷贝构造函数只有一个参数就是本类的引用。

    注意,默认构造函数(即无参构造函数)不一定存在,但是拷贝构造函数总是会存在。

    下面是一个拷贝构造函数的例子。

     1 #include<iostream>
     2 using namespace std;
     3 class A{
     4 public:
     5     int a;
     6     A(int value){
     7         a = value;
     8     }
     9     void show(){
    10         cout<<a<<endl;
    11     }
    12 }; 
    13 int main(){
    14     A test_a(10);
    15     test_a.show();
    16     
    17     A test_b(test_a);
    18     test_b.show();
    19     
    20     return 0;
    21 }

    输出结果为:

    10
    10

    如果编写了拷贝构造函数,则默认拷贝构造函数就不存在了。下面是一个非默认拷贝构造函数的例子。

     1 #include<iostream>
     2 using namespace std;
     3 class A{
     4 public:
     5     int a;
     6     A(int value){
     7         a = value;
     8     }
     9     A(A& tmp){
    10         a = tmp.a;
    11         cout<<"call copy construct"<<endl;
    12     }
    13     void show(){
    14         cout<<a<<endl;
    15     }
    16 }; 
    17 int main(){
    18     A test_a(10);
    19     test_a.show();
    20     
    21     A test_b(test_a);
    22     test_b.show();
    23     
    24     return 0;
    25 }

    输出结果为:

    10
    call copy construct
    10

    拷贝构造函数被调用的三种情况

    拷贝构造函数在以下三种情况下会被调用。

    1) 当用一个对象去初始化同类的另一个对象时,会引发拷贝构造函数被调用。例如,下面的两条语句都会引发拷贝构造函数的调用,用以初始化 test_b。

    1 A test_b(test_a);
    2 A test_b = test_a;

    这两条语句是等价的。

    注意,第二条语句是初始化语句,不是赋值语句。赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发拷贝构造函数的调用。例如:

    1 A test_a,test_b;
    2 test_b = test_a;

    这条语句不会引发拷贝构造函数的调用,因为  test_b  早已生成,已经初始化过了。

    2) 如果函数 F 的参数是类 A 的对象,那么当 F 被调用时,类 A 的拷贝构造函数将被调用。换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用拷贝构造函数时的参数,就是调用函数时所给的实参。

    3) 如果函数的返冋值是类 A 的对象,则函数返冋时,类 A 的拷贝构造函数被调用。换言之,作为函数返回值的对象是用拷贝构造函数初始化 的,而调用拷贝构造函数时的实参,就是 return 语句所返回的对象。例如下面的程序:

     1 #include<iostream>
     2 using namespace std;
     3 class A{
     4 public:
     5     int a;
     6     A(int value){
     7         a = value;
     8     }
     9     A(A& tmp){
    10         a = tmp.a;
    11         cout<<"call copy construct"<<endl;
    12     }
    13     void show(){
    14         cout<<a<<endl;
    15     }
    16 }; 
    17 A Func() {
    18     A test_a(4);
    19     return test_a;
    20 }
    21 int main(){
    22     Func().show(); 
    23     
    24     return 0;
    25 }

    输出结果:

    call copy construct
    4

    针对于第三条,有些编译器可能会有以下的结果:

    4

    这是因为编译器编译的时候进行了优化,函数返回值对象就不用拷贝构造函数初始化了,这其实并不符合 C++的标准。

    浅拷贝和深拷贝

    重头戏来了,内含指针的拷贝构造函数,C++是如何实现的呢,来看个例子:

     1 #include<iostream>
     2 using namespace std;
     3 class A{
     4 public:
     5         int a;
     6         int *p;
     7         A(int value1, int value2){
     8                 a = value1;
     9                 p = new int(value2);
    10         }
    11         ~A(){
    12                 delete p;
    13         }
    14 
    15         void show(){
    16                 cout<<a<<endl;
    17                 cout<<p<<endl;
    18                 cout<<*p<<endl;
    19         }
    20 };
    21 
    22 int main(){
    23         A test_a(10,20);
    24         test_a.show();
    25 
    26         A test_b(test_a);
    27         test_b.show();
    28 
    29         return 0;
    30 }

    输出结果如下:

    10
    0xf19010
    20
    10
    0xf19010
    20
    *** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000000f19010 ***
    ...

    可以看到对于class A 的对象 test_a 和 test_b 指针p 指向了同一块内存,在对象析构的时候被析构了两次导致了crash,这就是我们常说的浅拷贝。

    因此,在我们日常编写代码的时候特别需要注意这一点,对于指针我们需要相应的开辟一块新的内存,将指向的值拷贝过来,也就是所谓的深拷贝,下面是正确的写法:

     1 #include<iostream>
     2 using namespace std;
     3 class A{
     4 public:
     5     int a;
     6     int *p;
     7     A(int value1, int value2){
     8         a = value;
     9         p = new int(value2);
    10     }
    11     A(A& tmp){
    12         a = tmp.a;
    13         p = new int(* tmp.p);
    14     }
    15     ~A(){
    16         delete p;
    17     }
    18 
    19     void show(){
    20         cout<<a<<endl;
    21         cout<<p<<endl;
    22         cout<<*p<<endl;
    23     }
    24 }; 
    25 
    26 int main(){
    27     A test_a(10,20); 
    28     test_a.show();
    29     
    30     A test_b(test_a);
    31     test_b.show();
    32     
    33     return 0;
    34 }

    输出结果如下:

    10
    0xd4d010
    20
    10
    0xd4d030
    20
  • 相关阅读:
    JSON基础知识
    Java 环境配置
    接口测试基础知识
    Fiddler初学笔记
    es6数组方法findIndex()
    sass+less相关
    前端库/框架/插件相关
    知名博主相关
    CSS相关
    移动Web相关
  • 原文地址:https://www.cnblogs.com/r-yan/p/11727889.html
Copyright © 2011-2022 走看看