复制构造函数和隐式转换 ---临时变量问题
来源:互联网 发布:淘宝裤子追加评价语 编辑:程序博客网 时间:2024/06/05 01:08
什么是复制构造函数和隐式转换(类类型)?
什么时候会调用复制构造函数?
1.显示调用
直接调用.A a(100);
2."=" 赋值
3.函数形参的隐式转换,形参的复制.
4.初始化容器元素
5.构造函数和和数组元素
6.合成的复制构造函数
如果没有定义复制构造函数,编译器会给我们合成一个,即使有其他的构造函数存在,也会合成复制构造函数.
合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本.
"逐个成员"是指编译器将现有对象的每个非static成员,依次复制到正在创建的对象.
数组成员的复制是个例外.一般不能复制数组,但是如果一个类具有数组成员,则合成复制构造函数将复制数组.
这里有个疑问,如果已经存在合成复制构造函数,且,没有这个构造函数并没有初始化对象中的变量,此时,编译器是否会自动初始化为传入对象值的副本,或者只是初始化为默认值?
初始化为默认值.自己加了点代码测试了下.如果包含类类型,则会调用默认构造函数,如果是内置类型,初始化内置类型:
C++会对他们默认初始化,初始值可能是0(静态对象),可能是随机数(堆,栈对象).
这也同时说明了在构造函数初始化列表中初始化变量是件很重要的事.
2).定义自己的复制构造函数.
有些类必须对对复制对象时发生的事情加以控制.
有指针变量;
有成员在构造函数中分配地址.
禁止复制
为了防止复制,类必须显示声明其复制构造函数为private;
但类的友元和成员仍然可以复制,如果类的友元和成员也禁止复制时,可以声明但不定义复制构造函数.(声明而不定义成员函数是合法的,但使用未定义成员的额任何尝试将导致连接失败).
复制构造函数的最佳应用
下面的代码中包含了同时隐式转换和复制构造函数的调用.
在复制构造函数方面,下面一般情形分为两种方式.
1.
A a=右边;
2.
A a;
a=右边
查看以上两种情况到底怎么具体运行的.
代码和运行结果
#include <iostream>#include <vector>#include <string>using namespace std;class A{public:A(){cout<<"A with no para constructor:"<<this<<endl;}A(const A &a){cout<<"A with copy constructor:"<<this<<endl;}A(int i){value=i;cout<<"A with constructr int:"<<this<<endl;}~A(){cout<<"~A destructor:"<<this<<endl;}int value;};class B{public:B(){cout<<"B with no para constructor:"<<this<<endl;}~B(){cout<<"~B destructor:"<<this<<endl;}int i;A a;A arr[2];string *str;};A func(A temp){return temp;}int main(){cout<<"start to test>>"<<endl;A a;A b;b.value=-1;cout<<"condition 1.1>>"<<endl;//direct copya=b;cout<<"a:"<<a.value<<endl;//direct copy not means will use b,it will only call copy initialization cout<<"condition 1.2>>"<<endl;A e=b;cout<<"condition 2.1>>"<<endl;//initialiation c with int implicit expresstion.//first convert 102 to object A,then point c point 102 object;A c=102;cout<<"condition 2.2>>"<<endl;//first initial A named d,then assign int to d.//like c,then de autoA d;d=110;cout<<"d.value:"<<d.value<<endl;cout<<"start to test func>>"<<endl;cout<<"condition 3.1>>"<<endl;A a1;func(a1);cout<<"condition 3.2>>"<<endl;A b1;b1=func(a1);cout<<"condition 3.3>>"<<endl;A c1= func(a1);cout<<"condition 4>> test container"<<endl;vector <A> vec(5);cout <<"condition 5>> test array"<<endl;A arr[5]={A(),100};cout<<"condition 6>> test auto synthetic copy function"<<endl;B bc1;bc1.i=100;bc1.a=A();bc1.arr[0]=A();cout<<"test class point:"<<bc1.str<<endl;string temp("temp");bc1.str=&temp;B bc2=bc1;B bc3;bc3=bc1;cout<<"bc1:"<<bc1.i<<" "<<&bc1.a<<" "<<&bc1.arr[0]<<" "<<&bc1.arr[1]<<endl;cout<<"bc2:"<<bc2.i<<" "<<&bc2.a<<" "<<&bc2.arr[0]<<" "<<&bc2.arr[1]<<endl;cout<<"bc3:"<<bc3.i<<" "<<&bc3.a<<" "<<&bc3.arr[0]<<" "<<&bc3.arr[1]<<endl;cout<<"Test point:bc1.str:"<<bc1.str<<" bc2.str:"<<bc2.str<<" bc3.str:"<<bc3.str<<endl;cout<<"end to test"<<endl;return 0;}
运行结果:
start to test>>
A with no para constructor:0x7fffa5060b40
A with no para constructor:0x7fffa5060b50
condition 1.1>>
//这里应该是内存的拷贝,直接将内存单元中内容复制到例外内存单元中.
a:-1
condition 1.2>>
////这里调用的A类中的object A复制构造函数
A with copy constructor:0x7fffa5060b60
condition 2.1>>
//调用构造函数,隐式转换
A with constructr int:0x7fffa5060b70
condition 2.2>>
//这里先创建一个临时变量,然后内存复制,然后销毁.
A with no para constructor:0x7fffa5060b80
A with copy constructr int:0x7fffa5060b90
~A destructor:0x7fffa5060b90
d.value:110
start to test func>>
condition 3.1>>
////函数参数传递,传入一个临时变量,传出,一个临时变量.
A with no para constructor:0x7fffa5060ba0
A with copy constructor:0x7fffa5060bb0
A with copy constructor:0x7fffa5060bc0
~A destructor:0x7fffa5060bc0
~A destructor:0x7fffa5060bb0
condition 3.2>>
//这里可以猜测,函数传出后,直接内存复制给变量.然后函数传出的对象销毁.
A with no para constructor:0x7fffa5060bd0
A with copy constructor:0x7fffa5060be0
A with copy constructor:0x7fffa5060bf0
~A destructor:0x7fffa5060bf0
~A destructor:0x7fffa5060be0
condition 3.3>>
//注意比较这里,这里少一个析构函数.首先,函数参数传入,在函数栈中有个参数,传出的变量不在函数的栈中,而应该在主程序的栈中,上一个因为b1已经有了,分配了空间.所以传出的变为临时变量,传出的值内存复制给b1后,然后释放空间.而这里的c1,没有分配空间,于是将函数传出的值赋值给c1,使c1指向函数返回的内存地址.
A with copy constructor:0x7fffa5060c10
A with copy constructor:0x7fffa5060c00
~A destructor:0x7fffa5060c10
condition 4>> test container
//测试容器,容器会先创建一个临时变量,用临时变量初始化容器中的每个对象,调用复制构造函数,然后销毁临时对象.
A with no para constructor:0x7fffa5060c20
A with copy constructor:0x1810010
A with copy constructor:0x1810014
A with copy constructor:0x1810018
A with copy constructor:0x181001c
A with copy constructor:0x1810020
~A destructor:0x7fffa5060c20
condition 5>> test array
//测试类数组
//第一个对象用A()对象初始化
A with no para constructor:0x7fffa5060b10
//第二个调用int参数构造函数,隐式转换为A类型对象
A with constructr int:0x7fffa5060b14
//剩余是三个调用默认构造函数
A with no para constructor:0x7fffa5060b18
A with no para constructor:0x7fffa5060b1c
A with no para constructor:0x7fffa5060b20
//测试合成复制构造函数
condition 6>> test auto synthetic copy function
//这里说明一个问题:类先初始化成员变量后再调用的构造函数.所以打印输出时的顺序是先打印A,后打印B.
A with no para constructor:0x7fffa5060ab4
A with no para constructor:0x7fffa5060ab8
A with no para constructor:0x7fffa5060abc
B with no para constructor:0x7fffa5060ab0
//下面连续两个构造再析构,是对应代码中的两个赋值语句
A with no para constructor:0x7fffa5060c30
~A destructor:0x7fffa5060c30
A with no para constructor:0x7fffa5060c40
~A destructor:0x7fffa5060c40
test class point:0x2e9d40
//B bc2=bc1
//调用B合成的复制构造函数,初始化,同时,B类中的A类型对象也是调用A的复制构造函数
A with copy constructor:0x7fffa5060ad4
A with copy constructor:0x7fffa5060ad8
A with copy constructor:0x7fffa5060adc
//B bc3; bc3=bc1;
//调用bc3的默认构造函数,然后将内存中的bc1的信息拷贝到bc3中的内存中,所以只有默认构造函数输出.
A with no para constructor:0x7fffa5060af4
A with no para constructor:0x7fffa5060af8
A with no para constructor:0x7fffa5060afc
B with no para constructor:0x7fffa5060af0
//输出三个对象中地址和对应的值.类类型对象,生成的都是新的地址.
bc1:100 0x7fffa5060ab4 0x7fffa5060ab8 0x7fffa5060abc
bc2:100 0x7fffa5060ad4 0x7fffa5060ad8 0x7fffa5060adc
bc3:100 0x7fffa5060af4 0x7fffa5060af8 0x7fffa5060afc
//指针类型复制时,有点类似int等基本类型,直接复制指针单元的内容,三个对象的指针都指向一个地址.
//所以在定义复制构造函数中,有指针对象时,考虑到这个情况,并想想这个结果是不是我们想要的(只是复制指针变量中的地址,指向同一个地址),还是说再创建对象后,再用重新用指针指向.
Test point:bc1.str:0x7fffa5060b30 bc2.str:0x7fffa5060b30 bc3.str:0x7fffa5060b30
end to test
~B destructor:0x7fffa5060af0
~A destructor:0x7fffa5060afc
~A destructor:0x7fffa5060af8
~A destructor:0x7fffa5060af4
~B destructor:0x7fffa5060ad0
~A destructor:0x7fffa5060adc
~A destructor:0x7fffa5060ad8
~A destructor:0x7fffa5060ad4
~B destructor:0x7fffa5060ab0
~A destructor:0x7fffa5060abc
~A destructor:0x7fffa5060ab8
~A destructor:0x7fffa5060ab4
~A destructor:0x7fffa5060b20
~A destructor:0x7fffa5060b1c
~A destructor:0x7fffa5060b18
~A destructor:0x7fffa5060b14
~A destructor:0x7fffa5060b10
~A destructor:0x1810010
~A destructor:0x1810014
~A destructor:0x1810018
~A destructor:0x181001c
~A destructor:0x1810020
~A destructor:0x7fffa5060c00
~A destructor:0x7fffa5060bd0
~A destructor:0x7fffa5060ba0
~A destructor:0x7fffa5060b80
~A destructor:0x7fffa5060b70
~A destructor:0x7fffa5060b60
~A destructor:0x7fffa5060b50
~A destructor:0x7fffa5060b40
以上结果分析可以得出,在=和函数调用的时候会出现调用复制构造函数的问题.其中涉及了临时变量的问题.
看调用方式,如果,当前对象已经分配了地址空间,则调用复制构造函数时,会产生一个临时变量,临时变量由调用的复制构造函数生成.然后将其内存中的信息复制给当前对象.当前对象一直沿用之前的地址空间,而临时变量会调用析构函数释放空间.
如果没有分配空间,如A a=XXX.这时,调用复制构造函数,产生"临时A类型变量"时,a会指向这个产生的"临时变量".
A a;
A b;
a=func(b);
//相当于以下代码,假设func返回临时变量c
A c=func(b);
a=c;//这里是将c内存中的信息复制给a.
c析构;
或者这么说,对于赋值运算符 "=",左边为类类型.
如果在执行"="语句之前,"="两端的对象都有已经分配内存了,
则"="表明,
1)两边为同类型对象,两个内存之间的内容复制;
2)两遍非同类型对象,会调用相应的构造函数(如果存在的话),隐式转换成该类类型对象产生一个临时变量,然后临时变量与"="左边对象进行内存内容复制,然后,再释放临时对象内存.
如果"="语句之前,左端的对象并没有分配内存,如 A a=XXX 形式;
则会调用复制构造函数或者相应的构造函数(隐式转换),产生一个临时变量,然后这个临时变量初始化"="的左边.
对于函数调用返回,其实就是涉及两次复制,传入参数以及返回值.比较简单.
关于隐式转换
使用explicit关键字.具体:http://blog.csdn.net/cdhql/article/details/17447471
- 复制构造函数和隐式转换 ---临时变量问题
- 临时变量与复制构造函数
- C++ 学习之构造函数:构造函数小结3 默认构造函数、隐式类型转换和复制构造函数
- C++ 学习之构造函数:构造函数小结3 默认构造函数、隐式类型转换和复制构造函数
- C++ 学习之构造函数:构造函数小结3 默认构造函数、隐式类型转换和复制构造函数
- 临时变量与复制构造函数(二)
- 默认构造函数,隐式类型转换,复制构造函数
- 对象隐式转换,复制构造函数和重载等号运算符的区别
- effctive1 构造函数(默认,复制,防止隐式转换)
- 解释隐式转换赋值构造和复制构造等一些列问题(运行就明白了)
- 转换构造函数和隐式构造函数
- 复制构造函数问题
- c++ 赋值构造函数 临时变量 临时对象 之一
- C++转换构造函数和隐式转换函数
- C++转换构造函数和隐式转换函数
- C++转换构造函数和隐式转换函数
- C++转换构造函数和隐式转换函数
- C++转换构造函数和隐式转换函数
- 动态表单的验证和获取值
- 《Android开发卷——自定义日期选择器(二)》
- NOJ 1218 You are my brother
- 山理和山建大一新生友谊赛 E - Gold Coins
- HTML和CSS
- 复制构造函数和隐式转换 ---临时变量问题
- 【cocos2d-x 3.0】入门样例《SimpleGame》源码解读【1】
- IOS绘图详解
- 酒桌上的潜规则,男人必学,女人必知
- shell 脚本中将输出内容赋值给一个变量时的换行问题
- java线程内存模型,线程、工作内存、主内存
- 我的博客在这里开始
- Android编程碎片:Invalid layout of java.lang.String at value
- MongoDB集群及客户端读写分离实现