IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】拷贝/复制构造函数 -> 正文阅读

[C++知识库]【C++】拷贝/复制构造函数

文章目录

函数功能

函数使用

在主函数内如何写?

设计函数参数

什么情况下需要调用拷贝构造函数?

深拷贝与浅拷贝

刨析函数内构造、拷贝构造、析构函数的执行过程


函数功能

  • 功能
    • 在本类的旧对象复制新对象

函数使用

在主函数内如何写?

  • 定义新对象=旧对象;
    • 初始化旧对象
    • 再用旧对象对新对象初始化

例:

  • 假设定义了一个A类,在该类内定义对象a以及b
  • int main()
    {
        A a(3,6);
        A b=a;
    }
  • 这样就是正确写法, 调用拷贝构造,将a的值拷贝给新对象b
  • 但下面这种没有调用拷贝构造
  • int main()
    {
        A a(3,6);
        A c;
        c=a;
    }
  • 这样写不会调用拷贝构造,因为在第一步已经将c对象构造出来,而第二步是用a对c重新赋值

设计函数参数

  • 假设一个类为A类,该拷贝构造函数名与类名相同为A
  • 1.假设参数类型为值类型,如下代码所示:
    • A(A s)//拷贝构造函数A,参数为s
      {
      }
      int main()
      {
          A a;//调用构造函数定义a
          A b(a);//调用拷贝构造函数将a复制给b
      }
    • 调用时发现,值类型不行,因为在将a复制给b,调用拷贝构造函数A(),在实参a对形参s传递时,实参a需要对形参s进行初始化,就还需要调用拷贝构造函数A,这样会产生递归调用。

  • 2.假设参数类型为指针类型
    • A(A *s)//拷贝构造函数A,参数为s
      {
      }
      int main()
      {
          A a;//调用构造函数定义a
          A b=&a;//调用拷贝构造函数将a复制给b
      }
    • 指针作为参数类型时,实参传递时传的是值,形参s直接作为指针,直接指向实参a的空间即可,不需要对其开辟空间,也就不会产生递归调用。但是这样在主函数内的A b=&a看着会有歧义,就容易看做是把a的地址去初始化b

  • 3.假设参数类型为引用类型,这就是正确答案~
    • A(A &s)//拷贝构造函数A,参数为s
      {
      }
      int main()
      {
          A a;//调用构造函数定义a
          A b =a;//调用拷贝构造函数将a复制给b
      }
    • 为什么引用不需要对形参开辟空间,因为引用返回的是本身,也就不会产生递归调用。而A(A &s)就是拷贝构造函数的正确设计。

什么情况下需要调用拷贝构造函数?

  • 1.用已有对象去初始化本类的对象
    • class A
      {
      public:
          A(int i=0,int j=0):m_i(i),m_j(j)//构造函数
              cout<<"A"<<endl
          A(const A &s):m_i(s,m_i),m_j(m_j)//拷贝构造函数
              cout<<"A(A&)"<<endl;
      private:
          int m_i;
          int m_j;
      };
      
      void main()
      {
          A a(2,6);//调构造函数
          A b(a);//调用拷贝构造函数?
      }
      
       A b(a);//调用拷贝构造函数?
  • 2.函数传参,类类型的值传递
    • void Test(A t)
      {
          cout<<"Test:"<<endl;
      }
      
      void main()
      {
          A c(9,3);//调构造函数
          Test(c);//调test函数
      }
      
  • 3.是值返回的类类型的函数,从局部对象到临时时调用。
    • 
      A fn()
      {
          A d(10,12);
          return d;
      }
      
      void main()
      {
          A c(9,3);//调构造函数
          c=fn();
      }
      

  • ?有指针作为数据成员时,一般析构和拷贝构造函数程序员都要自己写

??深拷贝与浅拷贝

什么是浅拷贝:

  • ?类提供的默认拷贝构造函数是浅拷贝,只拷贝值,如有右指针作为数据成员,则会导致两个对象的指针指向同一段内存,那么在析构的时候会出问题(因为指向同一段内存,在第一次析构时,把空间释放了,而同一段空间不能被两次释放,就会报错),基于该问题就引出了深拷贝。

什么时候需要深拷贝?

  • 由指针作为数据成员,而且用本类的旧对象构造了新对象,则要写拷贝构造,给新对象的指针开辟和旧对象指针同样大小的内存单元,然后拷贝相同的值(不但拷贝值,而且拷贝资源)

如下代码所示:

  • 将s进行深拷贝
  • A(constA &s):m_i(s,m_i),m_j(m_i)
    {
        m_name=new char[strlen(s.i)+1];//给新对象指针开辟和旧对象相同大小的内存空间
        strcpy(m_i,s,m_i);//拷贝值
    }


刨析函数内构造、拷贝构造、析构函数的执行过程

用如下代码举例,分析输出结果:

class A
{
public:
    A(int i=0,int j=0):m_i(i),m_j(j)//构造函数
        cout<<"A"<<endl
    A(const A &s):m_i(s,m_i),m_j(m_j)//拷贝构造函数
        cout<<"A(A&)"<<endl;
    ~A()//析构函数
        cout<<"A(A&)"<<endl;
    pirint()
    {
        cout<<m_i<<" "<<m_j<<" "<<endl;
    }
private:
    int m_i;
    int m_j;
};

void Test(A t)
{
    cout<<"Test:"<<endl;
    t.print();//调用输出函数
}
A fn()
{
    A d(10,12);
    return d;
}

void main()
{
    A a(2,6);//调构造函数
    A b(a);//调用拷贝构造函数?
    A c(9,3);//调构造函数
    Test(c);//调test函数
    c=fn();
    cout<<"main end"<<endl;
}


执行结果:


解释:

  • 对象a在定义时依次调用1次构造函数A
  • 用a对b复制,调用1次拷贝构造函数A(A&)
  • 对象c在定义时依次调用1次构造函数A
  • Test函数内,将实参c传给Test内的形参t(形参开辟空间是在函数调用时开辟,接收实参值),从实参c到形参t,调用1次拷贝构造函数A(A&),以及输出一次函数内部的Test:,然后调用printf函数输出t的值,m_i=9,m_j=3;此时,t的作用域消失,析构t~A93
  • 在执行fn()时,fn内定义对象d,调用一次构造函数A;return d时,d作为局部变量,出了作用域fn()之后就不起作用了,因此需要用一个临时变量temp保存值,用局部对象temp将值带回给主函数。因此,在该阶段,首先d->temp,调用一次拷贝构造函数A(A&),然后d消失调用一次析构函数~A1012temp->带回到主函数c内,调用一次拷贝构造函数A(A&)(解释一下这个为什么没有输出,因为现在编译器都优化了,实际是调用了,但随着编译器逐渐高级,编译器对其优化,没有必要再执行拷贝这一步)然后temp消失调用一次析构函数~A1012;然后执行“main end”,最后,主函数内变量作用域消失,根据析构函数的顺序,先构造的后析构,依次是c,b,a,~A93,~A26,~A26

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:13:43  更:2022-10-17 12:17:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 7:07:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码