为什么不在此代码中调用复制构造函数

时间:2011-10-15 18:35:02

标签: c++ copy-constructor

那么为什么不在“ const Integer operator +(const Integer& rv)”函数中调用Copy构造函数。是因为RVO。 如果是,我需要做些什么来阻止它?

#include <iostream>

using namespace std;

class Integer {
    int i;

public:
    Integer(int ii = 0) : i(ii) {
        cout << "Integer()" << endl;
    }

    Integer(const Integer &I) {
        cout << "Integer(const Integer &)" << endl;
    }

    ~Integer() {
        cout << "~Integer()" << endl;
    }

    const Integer operator+(const Integer &rv) const {
        cout << "operator+" << endl;
        Integer I(i + rv.i);
        I.print();
        return I;
    }

    Integer &operator+=(const Integer &rv) {
        cout << "operator+=" << endl;
        i + rv.i;
        return *this;
    }

    void print() {
        cout << "i: " << i << endl;
    }
};

int main() {
    cout << "built-in tpes:" << endl;
    int i = 1, j = 2, k = 3;
    k += i + j;
    cout << "user-defined types:" << endl;
    Integer ii(1), jj(2), kk(3);
    kk += ii + jj;

}

我确实收到错误如果我要注释掉复制构造函数。我期望在operator +返回时调用复制构造函数。以下是程序的输出

built-in tpes:
user-defined types:
Integer()
Integer()
Integer()
operator+
Integer()
i: 3 // EXPECTING Copy Constructor to be called after this
operator+=
~Integer()
~Integer()
~Integer()
~Integer()

2 个答案:

答案 0 :(得分:4)

  

是因为RVO吗?如果是,我需要做些什么才能防止它?

是。但是由于编译器的Return Value Optimization没有调用它。

如果您正在使用GCC,请使用-fno-elide-constructors选项来避免它。

GCC 4.6.1 manual说,

  

-fno-elide-constructors

     

C ++标准允许实现省略创建临时文件,该临时文件仅用于初始化相同类型的另一个对象。指定此选项会禁用该优化,并强制G ++在所有情况下都调用复制构造函数。

答案 1 :(得分:1)

(N)RVO是最容易实现的优化之一。在大多数按值返回的调用约定中,调用者保留返回对象的空间,然后将隐藏指针传递给函数。然后函数构造给定地址中的对象。也就是说,kk += ii + jj;被翻译成:

Integer __tmp;

//                  __rtn  this  arg
Integer::operator+( &tmp,  &ii,  jj );

kk += __tmp;

该函数(在本例中为Integer::operator+采用第一个隐藏参数__rtn,该参数是指向sizeof(Integer)字节的未初始化内存块的指针,其中将构造对象,第二个隐藏参数this,然后是代码中函数的参数。

然后该函数的实现被翻译成:

Integer::operator+( Integer* __rtn, Integer const * this, const Integer &rv) {
    cout << "operator+" << endl;
    new (__rtn) Integer(i + rv.i);
    __rtn->print();
}

因为调用约定传递指针,所以函数不需要为随后复制的本地整数保留额外的空间,因为它可以直接在代码中构建I到接收的指针,并避免复制。

请注意,并非在所有情况下编译器都可以执行NRVO,特别是,如果函数中有两个本地对象,并且根据不能从代码中输入的条件返回任一个(比如参数的值)到功能)。虽然你可以这样做以避免RVO,但事实是它会使你的代码更复杂,效率更低,维护更难。