https://blog.youkuaiyun.com/pshizhsysu/article/details/40741291
一个对象在创建时,一定会调用某个构造函数进行构造。如果函数的参数是以按值传递,则会调用参数类型的拷贝构造函数对形参进行构造;如果返回值是值,则会对返回给函数调用者的对象调用拷贝构造函数进行构造。看下面的使用情况
- 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; }
- struct A{
- A(){}
- A(A&){}
- };
- A g(){ const A a; return a; } // Error
- const A f(){ A a; return a; } // OK
- 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; } // Error
- int main()
- {
- g().print();
- return 0;
- }
继续看下面的代码
- 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()结束 -> 主调函数继续执行