【问题标题】:Why deleting copy constructor affects user-defined default constructor?为什么删除复制构造函数会影响用户定义的默认构造函数?
【发布时间】:2018-02-24 22:30:24
【问题描述】:

学习 C++,关于构造函数的问题很少。

请考虑以下代码:

#include<stdio.h>
#include<iostream>

// Case 1

class CFoo
{
public:
    CFoo()  { printf("CFoo constructor: user-defined default\n"); }
    ~CFoo() { printf("CFoo destructor\n"); }
};

void testFoo()
{
    CFoo foo0;              // A way to use default constructor
    CFoo foo1 = CFoo();     // Another way to use default constructor
    CFoo foo2 = CFoo(foo1); // Using implicit copy constructor

    // Output:
    //     CFoo constructor: user-defined default
    //     CFoo constructor: user-defined default
    //     CFoo destructor
    //     CFoo destructor
    //     CFoo destructor
    //     CFoo destructor
}

// Case 2

class CBar
{
public:
    CBar() { printf("CBar constructor: user-defined default\n"); }
    CBar(CBar & other) = delete;
    ~CBar() { printf("CBar destructor\n"); }
};

void testBar()
{
    CBar bar0;
    // line 44: error C2280: 'CBar::CBar(CBar &)': attempting to reference a deleted function
    // line 34: note: see declaration of 'CBar::CBar'
    // line 34: note: 'CBar::CBar(CBar &)' : function was explicitly deleted
    // CBar bar1 = CBar(); // Why this has anything to do with the copy constructor?

    // Output:
    //     CBar constructor: user-defined default
    //     CBar destructor
}

// Case 3

class CBaz
{
public:
    CBaz()             { printf("CBaz constructor: user-defined default\n"); }
    CBaz(CBaz & other) { printf("CBaz constructor: user-defined copy\n"); }
    ~CBaz()            { printf("CBaz destructor\n"); }
};

void testBaz()
{
    CBaz baz0;
    CBaz baz1 = CBaz();
    CBaz baz2 = CBaz(baz1);

    // Output:
    //     CBaz constructor: user-defined default
    //     CBaz constructor: user-defined default
    //     CBaz constructor: user-defined copy
    //     CBaz destructor
    //     CBaz destructor
    //     CBaz destructor
}

// main

void main() {
    testFoo();
    testBar();
    testBaz();

    std::cin.get();
}

问题:

  1. 为什么我不能像使用CFoo 那样创建CBar 的实例为CBar bar1 = CBar();

  2. testFoo 调用 4 个析构函数。其中 3 个用于 foo0、foo1 和 foo2。 4th从哪里来? testBaz 具有相同的结构,但只调用了 3 个析构函数。 CFooCBaz 之间的唯一区别是 CBaz 具有用户定义的复制构造函数。

【问题讨论】:

  • CBar bar1 = CBar(); 不是赋值,而是使用复制/移动构造函数进行的初始化。

标签: c++ constructor copy destructor


【解决方案1】:
  1. 为什么我不能像使用CFoo 那样创建CBar 的实例为CBar bar1 = CBar();

CBar 的拷贝构造函数被删除。因此CBar 是不可复制的。因此你不能复制初始化CBar

CFoo是可复制的,所以没有问题。

CBar bar1 = CBar(); // Why this has anything to do with the copy constructor?

T object = other; 是复制初始化的语法。


  1. 第 4 个来自哪里?

除了命名对象(变量)之外,还构造了两个临时对象CFoo()CFoo(foo1)。无论出于何种原因,这些临时对象中只有一个通过复制省略进行了优化。

testBaz 具有相同的结构,但只调用了 3 个析构函数。

这在某种程度上影响了优化器。我看不出为什么两个 CFoo 的临时对象都不能被优化掉,但一个没有。值得一提的是,我的编译器确实优化了两者。


附言。 CBaz baz1 = CBaz(); 格式错误,因为非常量左值引用无法绑定到临时对象。

PPS。 void main() 格式错误,因为 main 必须返回 int

【讨论】:

  • 您能否详细说明为什么CBaz baz1 = CBaz(); 格式不正确?
  • @AlexXsWx CBaz() 是临时的。复制构造函数的参数类型为CBaz &amp;。它可能不受暂时的约束。由于复制构造函数不应该修改它的参数,你应该使用const CBaz &amp; 作为参数。 const 引用可能会绑定到临时对象。
【解决方案2】:

CBar bar1 = CBar(); 不是赋值,而是使用复制/移动构造函数进行的初始化。

即使省略,移动/复制构造函数也应该是可访问的。

自 C++17 以来,通过保证省略,规则发生了变化,并且 CBar bar1 = CBar(); 不需要移动/复制构造函数可以访问, 它相当于CBar bar1{};

【讨论】:

    【解决方案3】:

    这是直接初始化:

    CFoo foo0;
    

    这是复制初始化:

    CBaz baz1 = CBaz();
    

    由于copy elision,复制初始化在性能上变得等同于直接初始化,但在语义上则不然;复制初始化仍然需要一个可访问的复制构造函数。

    四个析构函数打印输出中的两个来自使用默认复制构造函数CFoo 构造的实例。请注意,打印输出的数量取决于优化:当复制省略优化打开时,中间情况的复制构造函数的调用,即CFoo foo1 = CFoo() 可以被优化出来,所以你得到三个打印输出,而不是四个。 CFoo foo2 = CFoo(foo0)右侧的副本没有优化出来。

    【讨论】:

      猜你喜欢
      • 2013-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-23
      • 1970-01-01
      • 2018-07-17
      • 2020-05-14
      • 1970-01-01
      相关资源
      最近更新 更多