详解C++ 拷贝构造函数和赋值运算符

 更新时间:2016年12月08日 14:12:55   作者:Brook_icv  
本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数、什么情况下调用赋值运算符。最后,简单的分析了下深拷贝和浅拷贝的问题。有需要的朋友可以看下

本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数、什么情况下调用赋值运算符。最后,简单的分析了下深拷贝和浅拷贝的问题。

拷贝构造函数和赋值运算符

在默认情况下(用户没有定义,但是也没有显式的删除),编译器会自动的隐式生成一个拷贝构造函数和赋值运算符。但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算。

class Person
{
public:
 Person(const Person& p) = delete;
 Person& operator=(const Person& p) = delete;
private:
 int age;
 string name;
};

上面的定义的类Person显式的删除了拷贝构造函数和赋值运算符,在需要调用拷贝构造函数或者赋值运算符的地方,会提示_无法调用该函数,它是已删除的函数_。

还有一点需要注意的是,拷贝构造函数必须以引用的方式传递参数。这是因为,在值传递的方式传递给一个函数的时候,会调用拷贝构造函数生成函数的实参。如果拷贝构造函数的参数仍然是以值的方式,就会无限循环的调用下去,直到函数的栈溢出。

何时调用

拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给另一个对象(已经存在的)。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

调用拷贝构造函数主要有以下场景:

  • 对象作为函数的参数,以值传递的方式传给函数。 
  • 对象作为函数的返回值,以值的方式从函数返回
  • 使用一个对象给另一个对象初始化

代码如下:

class Person
{
public:
 Person(){}
 Person(const Person& p)
 {
 cout << "Copy Constructor" << endl;
 }
 Person& operator=(const Person& p)
 {
 cout << "Assign" << endl;
 return *this;
 }
private:
 int age;
 string name;
};
void f(Person p)
{
 return;
}
Person f1()
{
 Person p;
 return p;
}
int main()
{
 Person p;
 Person p1 = p; // 1
 Person p2;
 p2 = p; // 2
 f(p2); // 3
 p2 = f1(); // 4
 Person p3 = f1(); // 5
 getchar();
 return 0;
}

上面代码中定义了一个类Person,显式的定义了拷贝构造函数和赋值运算符。然后定义了两个函数:f,以值的方式参传入Person对象;f1,以值的方式返回Person对象。在main中模拟了5中场景,测试调用的是拷贝构造函数还是赋值运算符。执行结果如下:

分析如下:

  1. 这是虽然使用了"=",但是实际上使用对象p来创建一个新的对象p1。也就是产生了新的对象,所以调用的是拷贝构造函数。
  2. 首先声明一个对象p2,然后使用赋值运算符"=",将p的值复制给p2,显然是调用赋值运算符,为一个已经存在的对象赋值 。
  3. 以值传递的方式将对象p2传入函数f内,调用拷贝构造函数构建一个函数f可用的实参。
  4. 这条语句拷贝构造函数和赋值运算符都调用了。函数f1以值的方式返回一个Person对象,在返回时会调用拷贝构造函数创建一个临时对象tmp作为返回值;返回后调用赋值运算符将临时对象tmp赋值给p2.
  5. 按照4的解释,应该是首先调用拷贝构造函数创建临时对象;然后再调用拷贝构造函数使用刚才创建的临时对象创建新的对象p3,也就是会调用两次拷贝构造函数。不过,编译器也没有那么傻,应该是直接调用拷贝构造函数使用返回值创建了对象p3。

深拷贝、浅拷贝

说到拷贝构造函数,就不得不提深拷贝和浅拷贝。通常,默认生成的拷贝构造函数和赋值运算符,只是简单的进行值的复制。例如:上面的Person类,字段只有int和string两种类型,这在拷贝或者赋值时进行值复制创建的出来的对象和源对象也是没有任何关联,对源对象的任何操作都不会影响到拷贝出来的对象。反之,假如Person有一个对象为int *,这时在拷贝时还只是进行值复制,那么创建出来的Person对象的int *的值就和源对象的int *指向的是同一个位置。任何一个对象对该值的修改都会影响到另一个对象,这种情况就是浅拷贝。

深拷贝和浅拷贝主要是针对类中的指针和动态分配的空间来说的,因为对于指针只是简单的值复制并不能分割开两个对象的关联,任何一个对象对该指针的操作都会影响到另一个对象。这时候就需要提供自定义的深拷贝的拷贝构造函数,消除这种影响。通常的原则是:

  • 含有指针类型的成员或者有动态分配内存的成员都应该提供自定义的拷贝构造函数
  • 在提供拷贝构造函数的同时,还应该考虑实现自定义的赋值运算符

对于拷贝构造函数的实现要确保以下几点:

  • 对于值类型的成员进行值复制
  • 对于指针和动态分配的空间,在拷贝中应重新分配分配空间
  • 对于基类,要调用基类合适的拷贝方法,完成基类的拷贝

总结

  • 拷贝构造函数和赋值运算符的行为比较相似,却产生不同的结果;拷贝构造函数使用已有的对象创建一个新的对象,赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符,主要是否有新的对象产生。
  • 关于深拷贝和浅拷贝。当类有指针成员或有动态分配空间,都应实现自定义的拷贝构造函数。提供了拷贝构造函数,最后也实现赋值运算符。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持脚本之家!

相关文章

  • Qt基础开发之Qt多线程类QThread与Qt定时器类QTimer的详细方法与实例

    Qt基础开发之Qt多线程类QThread与Qt定时器类QTimer的详细方法与实例

    这篇文章主要介绍了Qt基础开发之Qt多线程类QThread与Qt定时器类QTimer的详细方法与实例,需要的朋友可以参考下
    2020-03-03
  • C++中std::is_object的具体使用

    C++中std::is_object的具体使用

    std::is_object是一种C++类型特性,其用途是判断一个类型是否是一个对象类型,本文主要介绍了C++中std::is_object的具体使用,感兴趣的可以了解一下
    2024-01-01
  • 详解C++ cin.getline函数

    详解C++ cin.getline函数

    这篇文章主要介绍了C++ cin.getline函数的相关资料,帮助大家更好的理解和学习C++,感兴趣的朋友可以了解下
    2020-09-09
  • Qt编写地图综合应用之绘制雨量分布

    Qt编写地图综合应用之绘制雨量分布

    雨量分布图是在区域地图基础上,针对区域中的每个最小单位区域比如县城点位不同颜色显示。本文将详细为大家介绍如何通过QT编写绘制雨量分布,感兴趣的小伙伴可以了解一下
    2021-12-12
  • C语言中关于库函数 qsort 快排的用法

    C语言中关于库函数 qsort 快排的用法

    快速排序Qsort是所有学习算法和数据结构最基础的一个部分,也是考试题和面试的一个小重点。本片文章带你了解Qsort的详细用法规则
    2021-09-09
  • C++实现循环队列和链式队列的示例

    C++实现循环队列和链式队列的示例

    下面小编就为大家分享一篇C++实现循环队列和链式队列的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • OpenCV图像旋转Rotate的详细介绍

    OpenCV图像旋转Rotate的详细介绍

    这篇文章主要介绍了OpenCV图像旋转Rotate,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • C 语言中布尔值的用法实战案例

    C 语言中布尔值的用法实战案例

    这篇文章主要为大家介绍了C语言中布尔值的用法实战案例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 用C语言模仿Python函数的一种简单实现方法

    用C语言模仿Python函数的一种简单实现方法

    这篇文章主要介绍了用C语言模仿Python函数的一种简单实现方法,需要的朋友可以参考下
    2017-05-05
  • Qt实现简易计时器的示例代码

    Qt实现简易计时器的示例代码

    计时器实现四个功能:开始计时、停止计时、暂停计时以及打点。当点击暂停时,开始按钮和停止按钮无法点击。当点击停止时,开始按钮和暂停按钮无法点击,此时停止按钮变为清零。本文将用Qt实现这样的一个计时器,需要的可以参考一下
    2022-06-06

最新评论