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

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

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

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

 #include<iostream>
using namespace std;
class A{
public:
int a;
A(int value){
a = value;
}
void show(){
cout<<a<<endl;
}
};
int main(){
A test_a();
test_a.show(); A test_b(test_a);
test_b.show(); return ;
}

输出结果为:


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

 #include<iostream>
using namespace std;
class A{
public:
int a;
A(int value){
a = value;
}
A(A& tmp){
a = tmp.a;
cout<<"call copy construct"<<endl;
}
void show(){
cout<<a<<endl;
}
};
int main(){
A test_a();
test_a.show(); A test_b(test_a);
test_b.show(); return ;
}

输出结果为:

call copy construct

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

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

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

 A test_b(test_a);
A test_b = test_a;

这两条语句是等价的。

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

 A test_a,test_b;
test_b = test_a;

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

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

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

 #include<iostream>
using namespace std;
class A{
public:
int a;
A(int value){
a = value;
}
A(A& tmp){
a = tmp.a;
cout<<"call copy construct"<<endl;
}
void show(){
cout<<a<<endl;
}
};
A Func() {
A test_a();
return test_a;
}
int main(){
Func().show(); return ;
}

输出结果:

call copy construct

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


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

浅拷贝和深拷贝

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

 #include<iostream>
using namespace std;
class A{
public:
int a;
int *p;
A(int value1, int value2){
a = value1;
p = new int(value2);
}
~A(){
delete p;
} void show(){
cout<<a<<endl;
cout<<p<<endl;
cout<<*p<<endl;
}
}; int main(){
A test_a(,);
test_a.show(); A test_b(test_a);
test_b.show(); return ;
}

输出结果如下:

0xf19010

0xf19010

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

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

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

 #include<iostream>
using namespace std;
class A{
public:
int a;
int *p;
A(int value1, int value2){
a = value;
p = new int(value2);
}
A(A& tmp){
a = tmp.a;
p = new int(* tmp.p);
}
~A(){
delete p;
} void show(){
cout<<a<<endl;
cout<<p<<endl;
cout<<*p<<endl;
}
}; int main(){
A test_a(,);
test_a.show(); A test_b(test_a);
test_b.show(); return ;
}

输出结果如下:

0xd4d010

0xd4d030

c++ 拷贝构造函数(重点在内含指针的浅拷贝和深拷贝)的更多相关文章

  1. C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现

    指针悬挂: 问题:使用new申请的内存内存空间无法访问,也无法释放. 原因:直接对指向new申请的存储空间的指针变量进行赋值修改 后果:失去了原来的地址,原来的空间无法访问也无法释放,造成内存泄漏 还 ...

  2. C++之拷贝构造函数

    为什么要引入拷贝构造函数?(提出问题) 作用:创建一个对象的同时,使用一个已经存在的对象给另一个对象赋值 做比较:拷贝构造函数:对象被创建 +  用一个已经存在的对象 进行初始化 拷贝赋值函数:对象已 ...

  3. c++拷贝构造函数(深拷贝、浅拷贝)——转

    拷贝构造函数: 拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类的一个引用变量,该参数是const类型,不可变的.例如:类A的拷贝构造函数的形式为A(A& ...

  4. java拷贝构造函数

    浅拷贝就是指两个对象共同拥有同一个值,一个对象改变了该值,也会影响到另一个对象. 深拷贝就是两个对象的值相等,但是互相独立. 构造函数的参数是该类的一个实例.   Operator = 拷贝构造函数 ...

  5. C++对象模型的那些事儿之四:拷贝构造函数

    前言 对于一个没有实例化的空类,编译器不会给它默认生成任何函数,当实例化一个空类后,编译器会根据需要生成相应的函数.这类函数包括一下几个: 构造函数 拷贝构造函数 析构函数 赋值运算符 在上一篇博文C ...

  6. C++拷贝构造函数总结

    C++拷贝构造函数总结 目录: 拷贝构造函数的基础知识 拷贝构造函数的使用 拷贝构造函数的行为 1.拷贝构造函数的基础知识 拷贝构造函数(copy constructor)是构造函数,是拷贝已经存在的 ...

  7. C++有关拷贝构造函数(默认/浅/深拷贝构造函数)

    拷贝结构函数顾名思义就是复制对象. 先讲一下默认拷贝函数: 默认拷贝就是直接赋值,让程序调用默认拷贝结构函数. Student p1; Student p2 = p1//或者Student p2(p1 ...

  8. C++ 拷贝构造函数和赋值运算符

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

  9. C++ 为什么拷贝构造函数参数必须为引用?赋值构造函数参数也必须为引用吗?

    之前写拷贝构造函数的时候,以为参数为引用,不为值传递,仅仅是为了减少一次内存拷贝.然而今天看到一篇文章发现自己对拷贝构造的参数理解有误. 参数为引用,不为值传递是为了防止拷贝构造函数的无限递归,最终导 ...

随机推荐

  1. luogu P4408 [NOI2003]逃学的小孩

    题目描述 Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:"喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?"一听说要考试,Chris的父母就心 ...

  2. 基于webpack实现多html页面开发框架八 html引入图片打包和公共页面模块复用

    一.解决什么问题 1.html中img引入的图片地址没有被替换,找不到图片 2.html公共部分复用问题,如头部.底部.浮动层等 二.html中img引入图片问题解决 1.在index.html插入i ...

  3. HDU4918 Query on the subtree 点分治+树状数组

    bobo has a tree, whose vertices are conveniently labeled by 1,2,…,n. At the very begining, the i-th ...

  4. HDU2255 奔小康赚小钱钱(二分图-最大带权匹配)

    传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子 ...

  5. 简单了解一下K8S,并搭建自己的集群

    距离上次更新已经有一个月了,主要是最近工作上的变动有点频繁,现在才暂时稳定下来.这篇博客的本意是带大家从零开始搭建K8S集群的.但是我后面一想,如果是我看了这篇文章,会收获什么?就是跟着步骤一步一走吗 ...

  6. 基于Pact的契约测试

    背景 如今,契约测试已经逐渐成为测试圈中一个炙手可热的话题,特别是在微服务大行其道的行业背景下,越来越多的团队开始关注服务之间的契约及其契约测试. 什么是契约测试     关于什么是契约测试这个问题, ...

  7. 人工智能技术导论——逻辑程序设计语言PROLOG

    最近在复习人工智能导论,里面介绍了一种逻辑关系语言PROLOG,但这本书里面用到的编译器是Turbo PROLOG,这个编译器早就被淘汰了,我后来找的了它的升级版Visual PROLOG,但一些语法 ...

  8. scrapy的使用-Pipelines

    #------------------简单的对item操作方式----------------------------# import json class QsbkPipeline(object): ...

  9. 开局一张图,学一学项目管理神器Maven!

    Maven强大的Java工程构建工具,做Java开发时少了跟Maven打交道,之前在知乎上看到有人提问:"学Java开发需不需要学习Maven?",个人认为是必需要学的,这和工欲善 ...

  10. textarea增加字数监听且高度自适应(兼容IE8)

    1.封装方法: var textareaListener = { /*事件监听器兼容 * *attachEvent——兼容:IE7.IE8:不兼容firefox.chrome.IE9.IE10.IE1 ...