美文网首页
一文读懂C++拷贝构造函数?

一文读懂C++拷贝构造函数?

作者: TangFly | 来源:发表于2020-04-23 15:43 被阅读0次

C++面试中,经常有面试官问“请你说一下C++拷贝构造函数是什么?“。而我们在阅读STL等很多C++的源码时,我们也经常看到作者对拷贝构造函数的处理。可以说,在学习C++的过程中,拷贝构造函数的理解是衡量一个人对C++了解深度的一个必考点。答得好,恭喜你入门了,否则可以说你还没有入门。那么,在此我将总结下这块的相关知识给大家参考理解。下面,我将从3个方面来说明这个知识点。

1. 拷贝构造函数是什么?

先直接上一段拷贝构造函数的代码,如下:

class MString

{

     MString(const MString& other) 

    {

            m_pChar = new char[strlen(other.m_pChar)+1];

            assert( nullptr != m_pChar);

            strcpy(m_pChar, other.m_pChar);

     }

private:

     char*  m_pChar;        

};

拷贝构造函数的形式是: Class(const Class& other)。首先,它本质上就是一个构造函数;其次,它实现的方式是从一个其他同类对象复制过来的。所以,和普通构造函数不一样的是,它是从另一个类对象中”克隆“过来的。既然是”克隆“,那么就是涉及到2个问题:1)什么情况下需要这种方式的”克隆“?2)怎么”克隆“? 对此,下面继续回答这两个问题。

2. 拷贝构造函数什么时候会被用到?

拷贝构造函数的使用场景有3类。代码形式分别如下:

class X { ...... };

X x;

X y;

X yy = x; // 第1类:用存在的类X的对象x,来初始化一个新声明的类对象yy。这句话等同于X yy(x);

y = x; // 这里,就不是了,因为y是已经存在了,所以这里调用的是类的operator=()函数。

void call(X x);

void bar()

{

    X xx;

    call(xx); //第2类:类对象xx作为函数的传值参数。进函数call时,会发生值copy。函数内部就会先调用类的拷贝构造函数创造一个值copy,即一个新的类对象副本在函数内使用。

...

}

X  funcall() 

{

    X zz;

   //....

    return zz; //第3类:用类对象作为函数的返回值时,会先调用类的拷贝构造函数生成一个类副本对象,用作后面的返回值对象。然后再执行zz对象的析构。

}

对此,我们可以看到,类对象的初始化、函数传值和函数对象返回时,都是调用类的拷贝构造函数来完成的。这么看,怎么保证这个类对象被正确的”克隆“呢?下面回答这个问题。

3.拷贝构造函数怎么执行“克隆”的?

我们知道,拷贝构造函数我们一般可以不写的。如下类:

class 3DPoint 

{

    float  m_x;

    float  m_y;

    float m_z;

};

void test()

{

    3DPoint  p1;

    3DPoint p2 = p1;//这里,编译器会自动生成一个默认的拷贝构造函数来复制对象。

   .......

}

test()函数执行正常。那么如果将这个类改成:

class 3DPoint 

{

public:

    ~3DPoint()

   {

         if(m_x)

               delete m_x;

         if(m_y)

               delete m_y;

         if(m_z)

               delete m_z;

    }

    float *m_x;

    float  *m_y;

    float *m_z;

};

void test()

{

    3DPoint  p1;

    3DPoint p2 = p1;

   .......

}

函数test()执行正常吗?对此,我们必须了解下, 如果没有实现,那么编译器给我们生成的拷贝构造函数采用什么方式来copy对象内容呢? 这里实际上执行的是“Default Memberwise Initialization" 方式。什么意思?就是对象成员的默认初始化方式。如果类成员里有类对象,就执行这个类对象的默认初始化方式。举个例子:

class Cicle

{

float m_radius;

3DPoint m_center;

};

这个类,编译器自动生成的拷贝构造函数就是大概这么实现的:

Cicle(const Circle& other)

{

        m_radius = other.m_radius;

        m_center = 3DPoint(other.m_radius);//调用成员类的copy构造函数。依次递归,直到最底层基本类型数据。

}

那么我们发现,最终调用的是基本数据类型的复制方式。那么基本类型的复制,就是一块内存里面的数据内容复制,如果没有说明指定方式,那么复制方式就是直接一个位一个位内容的copy。由此,我们现在来看改造类后的第二个test()函数的执行过程。在函数返回时,会调用对象p1、p2的析构,2个析构函数都会进行内存释放操作。如果上面改成指针后,却没有拷贝构造函数时,那么默认编译器会自动生成一个,但是默认生成的拷贝构造函数执行对象“克隆”时,采用的是3DPoint 的默认拷贝构造函数。那么,3DPoint p2 = p1; 这句执行后, 对象p2的成员m_x, m_y,m_z 指针就是将p1 中的成员 m_x, m_y,m_z 指针内容直接copy过来, 这样,就造成了两个类对象的指针指向的是同样的内存地址。因此p1,p2两个对象的成员m_x,m_y,m_z指针指向的对象是完全一样的。在析构时,就会造成同一块内存被2次释放!对此,解决方案,就是重写copy构造函数。对涉及到的成员指针对象进行“深copy”,即自己进行内存copy。

总结: 拷贝构造函数的深入理解是C++入门的一个关键问题。对于一个类,尽可能的去自己实现其copy构造函数和operator=() 函数,而不要采用默认的生成函数。这样避免类内存方面的错误。

相关文章

  • C++ 构造函数,类的成员变量

    c++ 05 构造函数无参构造函数有参构造函数 拷贝构造函数 浅拷贝 深拷贝 类的成员变量 四类特殊的成员变量

  • 一文读懂C++拷贝构造函数?

    C++面试中,经常有面试官问“请你说一下C++拷贝构造函数是什么?“。而我们在阅读STL等很多C++的源码时,我们...

  • [C++之旅] 12 拷贝构造函数

    [C++之旅] 12 拷贝构造函数 拷贝构造函数的特点 如果没有自定义的拷贝构造函数则系统自动生成一个默认的拷贝构...

  • c++学习笔记2(GeekBand)

    拷贝构造、拷贝赋值和析构 c++中有Big Three三个特殊的函数,他们就是拷贝构造函数,拷贝赋值函数和析构函数...

  • C++ 拷贝控制(二) — 移动构造函数和移动赋值运算符

    相关文章: C++ 拷贝控制(一) — 析构函数、拷贝构造函数与拷贝赋值函数 C++ 引用类型 — 左值引用、常引...

  • C++ 拷贝构造函数浅析

    什么是拷贝构造函数:拷贝构造函数,顾名思义,就是在拷贝的时候调用的构造函数。 几个原则:C++ primer p4...

  • C++拷贝构造函数——难点

    拷贝构造函数 - C++详细 | 编程字典

  • C++:面向对象基础

    构造函数 C++中有三种构造函数:默认构造函数,有参构造函数,拷贝构造函数 类对象的初始化 括号法//默认构造函数...

  • c++第二周笔记

    C++ 第二周笔记 本周的内容比较多,主要介绍了三个重要函数: 拷贝构造、拷贝赋值、析构函数。 1.拷贝构造函数。...

  • 浅析c++三大函数--GeekBand

    浅析c++ 三大函数 三大函数的特殊性 c++三大函数指的是拷贝构造、拷贝赋值、析构函数。这3个函数比较特殊: 一...

网友评论

      本文标题:一文读懂C++拷贝构造函数?

      本文链接:https://www.haomeiwen.com/subject/rdkaihtx.html