2011-08-25 140 views
8

在书中泛型编程和STL(中国版),它说:构造函数或复制构造函数?

X x = X()将调用拷贝构造函数。

对我来说这似乎有点不可思议。我写这样的

#include <iostream> 

class Test { 

public: 

    Test() { 
     std::cout << "This is ctor\n"; 
    } 

    Test(const Test&) { 
     std::cout << "This is copy-ctor\n"; 
    } 

}; 

int main(int argc, char** argv) 
{ 

    Test t = Test(); 
    return 0; 
} 

输出一个测试程序是“这是构造函数”。好吧,现在我很困惑,这是对的?

回答

9

很小的是,临时是默认构造的,然后调用复制构造函数将其复制到对象t中。

然而,在实践中,副本可被优化出— 即使它具有副作用(控制台输出):

[n3290: 8.5/16]:[..]在某些情况下,一种实施方式是 允许通过直接将中间结果 构造到被初始化的对象中来消除直接初始化中固有的复制;见12.2,12.8。

和(在具有相同的子句中给出的示例一起):

[n3290: 12.2/2]:[..]的实现可以(2)传递之前,将使用临时在其中 构建X到f()使用X的副本 构造函数;或者,X(2)可能在用于保存参数的空间 中构建。 [..]

但拷贝构造函数仍然存在,即使它可能不会被调用。

无论如何,如果你用的优化编译关闭(或与海湾合作委员会,可能-fno-elide-constructors),您将看到:

This is ctor 
This is copy-ctor 
+2

在gcc中,你可能不得不使用'-fno-elide-constructors',因为即使'-O0'也不能阻止elision,我想。 –

+0

@Kerrek:谢谢! –

+0

即使不是微不足道的*副本也可以被省略,通过构造临时代替局部变量来消除副本。对象或副本的复杂性与该优化无关。 –

4

从理论上讲,X x = X()将调用默认的构造函数创建一个临时对象,使用复制构造函数将其复制到x中。

实际上,编译器可以直接跳过复制构造部分并默认构造x(正如David在他的评论中指出的那样,仍然需要复制构造函数在语法上可访问)。大多数编译器至少在启用优化时执行此操作。

+4

重要的是要注意,拷贝构造函数必须是可用的,即使拷贝没有被删除也是如此。也就是说,如果它不可访问,编译器会拒绝错误的行。 –

+0

@David:你说得对。我已经将其纳入我的答案。感谢您提出这个问题。 – sbi

2

这种情况下,Return Value Optimisation(RVO)(也称为Copy Elision)的形式可以帮助您优化很多。链接的维基百科页面对发生的事情有非常好的解释。

+3

我不认为这是严格正确的。这里没有函数调用返回一个'X'(记住构造函数没有返回值)。这是复制elision,这是一个相关的,但独特的概念。 –

+0

我同意。这不是严格的RVO(尽管它是相关的)。 –

+0

@Tomak:这也不完全错误。这是*那种*优化的变体。 +1来抵消downvote。 –