20
class MyClass
{
public:
  ~MyClass() {}
  MyClass():x(0), y(0){} //default constructor
  MyClass(int X, int Y):x(X), y(Y){} //user-defined constructor
  MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){} //copy constructor

private:
  int x; int y;
};

int main()
{
  MyClass MyObj(MyClass(1, 2)); //user-defined constructor was called.
  MyClass MyObj2(MyObj); //copy constructor was called.
}

在第一种情况下,当MyClass(1, 2)调用用户定义的构造函数并返回一个对象时,我期望MyObj调用复制构造函数。为什么不需要为 的第二个实例调用复制构造函数MyClass

4

4 回答 4

40

每当临时对象被创建的唯一目的是被复制并随后被销毁时,编译器就可以完全删除临时对象并直接在接收者中构造结果(即直接在应该接收副本的对象中)。在你的情况下

MyClass MyObj(MyClass(1, 2));

可以转化为

MyClass MyObj(1, 2);

即使复制构造函数有副作用。

这个过程称为复制操作的省略。它在语言标准的 12.8/15 中有所描述。

于 2010-09-07T23:40:45.770 回答
19

在这种情况下,可以省略复制构造函数。

同样与MyClass MyObj = MyClass( 1, 2 );.

std::string str = "hello";

这样的代码有一个隐式的构造函数调用来将 转换char*std::string.

std::string str = std::string( "hello" ); // same, written more verbosely

如果没有复制省略,通过赋值语法进行的“简单”字符串初始化将产生额外的深层复制。该语法与您所拥有的语法有 99% 的等效性。

于 2010-09-07T23:37:40.187 回答
5

除了 Potatoswatter 和 Andrey T. 所说的之外,请注意,您可以哄骗大多数编译器不要省略构造函数。GCC 通常为您提供-fno-elide-constructors和 MSVC,/Od它应该为您提供所需的输出。这是一些代码:

#include <iostream>

#define LOG() std::cout << __PRETTY_FUNCTION__ << std::endl // change to __FUNCSIG__ on MSVC > 2003

class MyClass
{
public:
  ~MyClass() { LOG(); }
  MyClass():x(0), y(0){LOG(); } //default constructor
  MyClass(int X, int Y):x(X), y(Y){LOG(); } //user-defined constructor
  MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){LOG(); } //copy constructor

private:
int x; int y;
};

int main()
{
 MyClass MyObj(MyClass(1, 2)); //User-defined constructor was called.
 MyClass MyObj2(MyObj); //Copy constructor was called.
}

在 MingW32 上使用 GCC 4.5.0 编译:

 g++ -Wall -pedantic -ansi -pedantic tmp.cpp -o tmp -fno-elide-constructors

输出:

$ tmp.exe
MyClass::MyClass(int, int)
MyClass::MyClass(const MyClass&)
MyClass::~MyClass()
MyClass::MyClass(const MyClass&)
MyClass::~MyClass()
MyClass::~MyClass()
于 2010-09-08T00:01:59.273 回答
2

是什么让你认为它没有被调用?试试这个[编辑:更改代码以使用私有副本 ctor,因为即使省略了副本 ctor 的使用,也必须检查可用性]:

class MyClass
{
public:
   ~MyClass() {}
   MyClass():x(0), y(0){} //default constructor
   MyClass(int X, int Y):x(X), y(Y){} //user-defined constructor

private:
   MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){} //copy constructor
   int x; int y;
};

int main()
{
    MyClass MyObj(MyClass(1, 2)); //User-defined constructor was called.
    MyClass MyObj2(MyObj); //Copy constructor was called.
}

尝试编译它会在以下行中出现错误main

myclass.cpp(17) : error C2248: 'MyClass::MyClass' : cannot access private member
 declared in class 'MyClass'
        myclass.cpp(11) : see declaration of 'MyClass::MyClass'
        myclass.cpp(4) : see declaration of 'MyClass'
myclass.cpp(18) : error C2248: 'MyClass::MyClass' : cannot access private member
 declared in class 'MyClass'
        myclass.cpp(11) : see declaration of 'MyClass::MyClass'
        myclass.cpp(4) : see declaration of 'MyClass'

从概念上讲,在这两种情况下都使用了复制 ctor,编译器有义务检查它是否可访问。然而,在第一种情况下,编译器可以自由地省略对复制 ctor 的实际使用,只要它能够使用它。

于 2010-09-07T23:40:07.670 回答