在什么情况下调用C++拷贝构造函数?

Pan*_*rei 30 c++ constructor copy-constructor visual-c++

我知道c ++中的以下情况,其中将调用复制构造函数:

  1. 当为现有对象分配其自己的类的对象时

    MyClass A,B;
    A = new MyClass();
    B=A; //copy constructor called 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果函数接收作为参数,按值传递,则为类的对象

    void foo(MyClass a);
    foo(a); //copy constructor invoked
    
    Run Code Online (Sandbox Code Playgroud)
  3. 当函数返回(按值)类的对象时

    MyClass foo ()
       {
          MyClass temp;
          ....
          return temp; //copy constructor called
       } 
    
    Run Code Online (Sandbox Code Playgroud)

请随时纠正我所犯的任何错误; 但是如果有任何其他情况需要调用复制构造函数,我会更好奇.

0x4*_*2D2 24

当为现有对象分配其自己的类的对象时

    B = A;
Run Code Online (Sandbox Code Playgroud)

不必要.这种赋值称为复制赋值,这意味着将调用类的赋值运算符以执行所有数据成员的成员赋值.实际的功能是MyClass& operator=(MyClass const&)

这里不调用copy-constructor.这是因为赋值运算符接受对象的引用,因此不执行复制构造.

复制分配与复制初始化不同,因为复制初始化仅在初始化对象时完成.例如:

T y = x;
  x = y;
Run Code Online (Sandbox Code Playgroud)

第一个表达式y通过复制初始化x.它调用copy-constructor MyClass(MyClass const&).

如上所述,x = y是对赋值运算符的调用.

(还有一些叫做copy-elison的东西,编译器会忽略对copy-constructor的调用.你的编译器很可能会使用它).


如果函数接收作为参数,通过值传递,类的对象

    void foo(MyClass a);
    foo(a);
Run Code Online (Sandbox Code Playgroud)

这是对的.但是,请注意,在C++ 11中,如果a是xvalue并且MyClass具有相应的构造函数MyClass(MyClass&&),则a可以将其移动到参数中.

(复制构造函数和移动构造函数是类的默认编译器生成的两个成员函数.如果您自己不提供它们,编译器将在特定情况下慷慨地为您执行此操作).


当函数返回(按值)类的对象时

    MyClass foo ()
    {
        MyClass temp;
        ....
        return temp; // copy constructor called
    }
Run Code Online (Sandbox Code Playgroud)

通过返回值优化,如某些答案中所述,编译器可以删除对copy-constructor的调用.通过使用编译器选项-fno-elide-constructors,您可以禁用copy-elison并查看在这些情况下确实会调用copy-constructor.

  • @chenlian现在我回到这个答案,我发现它有点不准确。如果没有启用-fno-elide-constructors,那么实际上它是* move-constructor *,如果有的话,它首先被调用,如果没有,则调用copy-constructor。原因是MyClass&ref = temp; return ref之所以调用copy-constructor是因为返回值优化需要一个id表达式。在这种情况下,您需要显式的`std :: move`。 (3认同)

BWG*_*BWG 21

我可能错了,但是这个类可以让你看到被调用的内容和时间:

class a {
public:
    a() {
        printf("constructor called\n");
    };  
    a(const a& other) { 
        printf("copy constructor called\n");
    };    
    a& operator=(const a& other) {
        printf("copy assignment operator called\n");
        return *this; 
    };
};
Run Code Online (Sandbox Code Playgroud)

那么这段代码:

a b; //constructor
a c; //constructor
b = c; //copy assignment
c = a(b); //copy constructor, then copy assignment
Run Code Online (Sandbox Code Playgroud)

产生这个结果:

constructor called
constructor called
copy assignment operator called
copy constructor called
copy assignment operator called
Run Code Online (Sandbox Code Playgroud)

另一个有趣的事情,比如你有以下代码:

a* b = new a(); //constructor called
a* c; //nothing is called
c = b; //still nothing is called
c = new a(*b); //copy constructor is called
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为当您指定指针时,对实际对象不执行任何操作.

  • 还有一个`ac = b;`也称为复制构造函数 (6认同)
  • 我的代码并不是为了演示所有可能的事件,它显示了一个可用于查看事件的类. (2认同)

Bat*_*eba 12

情境(1)不正确,不会按照您编写的方式编译.它应该是:

MyClass A, B;
A = MyClass(); /* Redefinition of `A`; perfectly legal though superfluous: I've
                  dropped the `new` to defeat compiler error.*/
B = A; // Assignment operator called (`B` is already constructed)
MyClass C = B; // Copy constructor called.
Run Code Online (Sandbox Code Playgroud)

你是正确的(2).

但是在情况(3)中,可能不会调用复制构造函数:如果编译器可以检测到没有副作用,那么它可以实现返回值优化以优化不必要的深层复制.C++ 11使用右值引用将其形式化.


Lig*_*ica 6

这基本上是正确的(除了#1中的错字).

需要注意的另一个特定方案是,当容器中有元素时,可以在不同时间复制元素(例如,在向量中,当向量增长或删除某些元素时).这实际上只是#1的一个例子,但很容易忘记它.


Aks*_*hay 5

有三种情况可以调用复制构造函数:当我们复制一个对象时.当我们通过值将对象作为参数传递给方法时.当我们按值从方法返回一个对象时.

这是唯一的情况......我想......