• C++的拷贝构造函数、operator=运算符重载,深拷贝和浅拷贝、explicit关键字


    原文地址:https://blog.csdn.net/shine_journey/article/details/53081523

    1、在C++编码过程中,类的创建十分频繁。

    简单的功能,当然不用考虑太多,但是从进一步深刻理解C++的内涵,类的结构和用法,编写更好的代码的角度去考虑,我们就需要用到标题所提到的这些内容。

    2、先上代码

     
    1. // testSingleMode.cpp : 定义控制台应用程序的入口点。  
    2. //  
    3. #include "stdafx.h"  
    4. #include <iostream>  
    5.   
    6. using namespace std;  
    7.   
    8. class Complex  
    9. {  
    10. private:  
    11.     double m_real;  
    12.     double m_imag;  
    13.   
    14. public:  
    15.     Complex(void){  
    16.         m_real = 0.0;  
    17.         m_imag = 0.0;  
    18.     }  
    19.   
    20.     Complex(double real, double imag){  
    21.         m_real = real;  
    22.         m_imag = imag;  
    23.     }  
    24.   
    25.     Complex(const Complex & c){ //这里就是最经典的拷贝构造函数了  
    26.         m_real = c.m_real;  
    27.         m_imag = c.m_imag;  
    28.     }  
    29.   
    30.     Complex &operator = (const Complex &rhs){   //这里就是最经典的operator=操作符重载了  
    31.         if (this == &rhs){  
    32.             return *this;  
    33.         }  
    34.   
    35.         this->m_real = rhs.m_real;  
    36.         this->m_imag = rhs.m_imag;  
    37.   
    38.         return *this;  
    39.     }  
    40.   
    41.     explicit Complex::Complex(double r){    //explicit的用法,只适用于1个参数的情况  
    42.         m_real = r;  
    43.         m_imag = 0.0;  
    44.     }  
    45.   
    46. };  
    47.   
    48.   
    49. int main()  
    50. {  
    51.     Complex c1, c2; //调用 第15行 默认无参数的构造函数  
    52.     Complex c3(1.0, 2.5);   //调用 第20行 具有2个形参的构造函数  
    53.     //Complex c3 = Complex(1.0, 2.5);   //和上一行是一个意思,所以这个注释了  
    54.     c1 = c3;    //调用 第30行 重载operator=运算符  
    55.     c2 = c3;    //调用 第30行 重载operator=运算符  
    56.     //c2 = 5.2; //隐式转换,需要去掉41行的explicit关键字,才可编译通过  
    57.   
    58.     Complex c5(c2);     //调用 第25行 拷贝构造函数  
    59.     Complex c4 = c2;    //调用 第25行 拷贝构造函数  
    60.   
    61.     getchar();  
    62.     return 0;  
    63. }  

    【注1】explicit 只适用于构造函数只含有1个参数的情况,加上这个关键字,意味着不支持构造函数隐式转换,可以避免一些误解。如果去掉这个关键字,那么代码里面的:c2 = 5.2 ; 就是可以执行的了。

    【注2】为什么函数中可以直接访问对象c的私有成员?
    答:(网上)因为拷贝构造函数是放在本身这个类里的,而类中的函数可以访问这个类的对象的所有成员,当然包括私有成员了。

    3、上面代码的注释,已经是非常的清楚了

    自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,防止出错。

    浅拷贝:如果自己不写拷贝构造函数,系统会默认生成一个,而系统的拷贝构造函数是浅拷贝

    深拷贝:自己写一个拷贝构造函数,系统就不会产生了默认的构造函数了(来自网上说法)。自己写的这个拷贝构造函数,当然会有开辟空间的动作,所以是深拷贝。也就是说,如果生成类的实例的时候,调用了自己写的拷贝构造函数,那么在内存空间上,必然是会开辟新的空间,而不用担心只是一个指针。

     在某些状况下,类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

    4、总结

    (1)深拷贝:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。反之,没有重新分配资源,就是浅拷贝。

    (2)什么时候用到拷贝构造函数

      a.一个对象以值传递的方式传入函数体; 
      b.一个对象以值传递的方式从函数返回;
      c.一个对象需要通过另外一个对象进行初始化。

    (4)代码例子

     
    1. #include "stdafx.h"  
    2. #include <iostream>  
    3. #include <string.h>  
    4. #include <stdio.h>  
    5. #include <stdlib.h>  
    6. using namespace std;  
    7.   
    8. class Person  
    9. {  
    10. public:  
    11.   
    12.     // 构造函数  
    13.     Person(char * pN)  
    14.     {  
    15.         cout << "一般构造函数被调用 ! ";  
    16.         m_pName = new char[strlen(pN) + 1];  
    17.         //在堆中开辟一个内存块存放pN所指的字符串  
    18.         if (m_pName != NULL)  
    19.         {  
    20.             //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它  
    21.             strcpy_s(m_pName, strlen(pN) + 1, pN);  
    22.         }  
    23.     }  
    24.   
    25.     // 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员  
    26.     Person(Person & chs)  
    27.     {  
    28.         cout << "拷贝构造函数被调用 ! ";  
    29.         // 用运算符new为新对象的指针数据成员分配空间  
    30.         m_pName = new char[strlen(chs.m_pName) + 1];  
    31.   
    32.         if (m_pName)  
    33.         {  
    34.             // 复制内容  
    35.             strcpy_s(m_pName, strlen(chs.m_pName) + 1, chs.m_pName);  
    36.         }  
    37.   
    38.         // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了  
    39.     }  
    40.   
    41.     //// 系统创建的默认复制构造函数,只做位模式拷贝  
    42.     //Person(Person & p)  
    43.     //{  
    44.     //  //使两个字符串指针指向同一地址位置           
    45.     //  m_pName = p.m_pName;  
    46.     //}  
    47.   
    48.     ~Person()  
    49.     {  
    50.         delete m_pName;  
    51.     }  
    52.   
    53.     void getName(){  
    54.         cout << m_pName << endl;  
    55.     }  
    56.   
    57. private:  
    58.     char * m_pName;  
    59. };  
    60.   
    61. void main()  
    62. {  
    63.     Person man("lujun");  
    64.     man.getName();  
    65.     Person woman(man);  
    66.     woman.getName();  
    67.   
    68.     getchar();  
    69. }  


    程序运行环境: VS2013 

    输出结果:

  • 相关阅读:
    如何才算掌握JavaSE?
    JAVA学习之路:不走弯路,就是捷径
    让IT人远离慢性疲劳,长时间操作电脑需要养成的几个好习惯
    成为Java高手的25个学习目标非常经典
    程序员如何走到金字塔最高层
    jQuery强大的jQuery选择器 (详解)[转]
    不学必悔
    nginx的核心配置
    java中使用MemCached
    java 使用反射中的几个方法区别
  • 原文地址:https://www.cnblogs.com/ljwan1222/p/8869744.html
Copyright © 2020-2023  润新知