拷贝构造函数 参数传递 返回值

来源:互联网 发布:淘宝客卖家后台登陆 编辑:程序博客网 时间:2024/05/14 15:55

一个对象在创建时,一定会调用某个构造函数进行构造。如果函数的参数是以按值传递,则会调用参数类型的拷贝构造函数对形参进行构造;如果返回值是值,则会对返回给函数调用者的对象调用拷贝构造函数进行构造。看下面的使用情况

struct A{    A(){ cout << "A()" << endl; }    A(const A&){ cout << "A(const A&)" << endl; }    void print(){ cout << "print()" << endl; }}void f(A a){ a.print(); }A g(){ A a; return a; }void h(){ g().print(); }

上面的代码中,f 函数是按值传递,那么当这样子调用函数f 时 ’f(a1)‘ ,f 函数的参数a是如何被构造的呢,就是通过调用拷贝构造函数a(a1)对a进行构造。g()函数是返回一个值,在h()函数中 'g().print()' 的 'g()' 实质上是一个匿名的A对象(假设这个匿名对象叫a1),这个对象是如何被构造的呢,就是通过调用拷贝构造函数a1(a)进行构造的。

返回值

case 1:以下代码没错

struct A{A(){}A(const A&){}};A g(){ const A a;  return a; }const A f(){ A a; return a; }
case 2:以下代码有错,因为a 是const A类型,那么返回给g()函数的调用者的匿名对象调用的拷贝构造函数是 A(const A&),但该函数未定义。

struct A{A(){}A(A&){}};A g(){ const A a;  return a; } // Errorconst A f(){ A a; return a; } // OK
case 3:

struct A{    A(){ cout << "A()" << endl; }    A(const A&){ cout << "A(const A&)" << endl; }~A(){ cout << "~A()" << endl; }    void print(){ cout << "print()" << endl; }};A g(){ const A a; return a; }int main(){g().print();return 0;}
输出

A()

print()

~A()

对于输出,我感到很奇怪。我觉得输出应该是 A() A(const A&) ~A() print() ~A()。我的想法是,g 函数中的a 在g 函数退出时应该就要被析构,所以应该在print 函数前,而a析构前应该要用它作为参数构造返回的匿名对象,所以~A()之前应该有一个A(const A&),而匿名对象在main()函数结束时也应该被析构,所以最后应该也有一个~A()。

虽然上面的输出没有 A(const A&),但是还是有调用拷贝构造函数的,编译器做了优化,没有执行它的函数体,我们可以通过下面的代码进行验证

struct A{    A(){ cout << "A()" << endl; }    A(A&){ cout << "A(A&)" << endl; } // changed~A(){ cout << "~A()" << endl; }    void print(){ cout << "print()" << endl; }};A g(){ const A a; return a; } // Errorint main(){g().print(); return 0;}
当改了拷贝构造函数后,编译通不过,提示找不到拷贝构造函数 A(const A&),原因就是case 2。
继续看下面的代码

struct A{    A(){ cout << "A()" << endl; }    A(const A&){ cout << "A(const A&)" << endl; }~A(){ cout << "~A()" << endl; }    void print(){ cout << "print()" << endl; }};const A g(){ A a; cout << (void*)(&a) << endl; return a; }void f(const A& a1){ cout << (void*)(&a1) << endl; }int main(){    f(g());}

输出

A()

0x22ac2f
0x22ac2f

~A()

惊奇地发现,a1与a居然在同一地址上,也就是它们是同一个对象。这个时候我们可以大胆地推测,返回值会匹配拷贝构造函数,但是编译器做了优化后不会执行函数体。编译器应该是这样子认为的:析构g 函数中的a,再拷贝构造一个a1,效率不好,还不如把a直接给a1好了。但是要值得注意的是,拷贝构造函数还是一定要严格匹配的,而且虽然a和a1是同一个对象,但是修饰不一样,a是A类型,a1是const A类型,所以a可以调用print函数,a1不能调用print函数。

在 explicit 文章中也提到过, A  a  = A();只调用了默认构造函数,没有调用拷贝构造函数(但还是匹配拷贝构造函数的原型),编译器应该是认为,A()这个匿名对象,因为匿名的原因,其他地方也无法用到它了,既然要用它来拷贝构造另一个对象,不如把它直接给用一个对象好了。但是,A a = a1;就是调用拷贝构造函数了,因为a1还可能被用到。

但是,当返回的值不是函数声明的返回值类型却有相应该的转换构造函数时,情况怎样?且看下面代码

struct B{B(){ cout << "B()" << endl;}~B(){ cout << "~B()" << endl; }};struct A{    A(){ cout << "A()" << endl; }    A(const A&){ cout << "A(const A&)" << endl; }A(const B&){cout << "A(const B&)" << endl; }~A(){ cout << "~A()" << endl; }    void print(){ cout << "print()" << endl; }};A g(){ B b; cout << (void*)&b << endl; return b; }void f(const A& a){ cout << (void*)(&a) << endl; }int main() { A a = g();cout << (void*)&a << endl;}
输出:

B()
0x22abff
A(const B&)
~B()
0x22ac2f
~A()
可以看出,a与b指向不同的对象了。由上面的代码我们可以进一步肯定,函数function()返回时的一般步骤为

调用相应的构造函数构造function()返回的匿名对象 -> 析构function()中的对象 -> function()结束 -> 主调函数继续执行


0 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 搜狗输入法数字序号超过20怎么办 苹果手机保存的图片变模糊怎么办 微信视频保存到手机变模糊怎么办 自己的位置被别人取代了怎么办 给工厂做半成品老板跑了怎么办 微信变成英文再恢复汉字怎么办 cad中标注尺寸数字太小怎么办 扣扣的钱包手势密码忘记了怎么办 台式电脑带符号的数字打不出怎么办 情侣之间出现看见对方就烦怎么办 电信卡号和联通卡号怎么办情侣号 电脑能登qq但打不开网页怎么办 想跟朋友聊天但对方不理怎么办 刚进婆家门被婆婆欺负怎么办 支付宝的聊天记录被删了怎么办 彩票站买彩票把钱付了没出票怎么办 与异性朋友聊天没话题了怎么办 快递写错地址但已经发货了怎么办 快递写错电话但已经发货了怎么办 微信添加好友功能被限制怎么办 qq号被冻结了限制解封怎么办 被别人强制拉入qq群怎么办 qq群里的图片过期了怎么办 q附近人不能关注不能发信息怎么办 qq畅聊之火掉了怎么办 打印机打印时上面空白留太多怎么办 发短信一直空格里面写0怎么办 网贷获取我新手机号通讯录怎么办 系统音频驱动异常或未安装怎么办 附近功能已屏蔽你的qq好友怎么办 新申请的qq号忘了怎么办 刚申请的qq号忘了怎么办 以前申请的qq号忘了怎么办 小孩玩手机游戏扣费了怎么办 手机qq好友头像显示不出来怎么办 qq的一些重要数据被清理怎么办 华为p9微信听筒声音小怎么办 win10我的电脑图标没了怎么办 小米5x里的微信头像不清楚怎么办 微信头像换了总是模糊怎么办 找到老公暧昧对象的微信怎么办