是否为数组/向量插入调用了赋值运算符或复制构造函数?

Posted

技术标签:

【中文标题】是否为数组/向量插入调用了赋值运算符或复制构造函数?【英文标题】:Is Assignment Operator or Copy Constructor invoked for array/vector insertions? 【发布时间】:2018-10-24 19:04:53 【问题描述】:

语言:C++

我的问题是在以下情况下是否调用了复制构造函数或赋值运算符。在此之前,我理解以下内容:

MyClass a(3);      // single param constructor
MyClass b(a);      // copy constructor invoked
MyClass c = b;     // copy constructor invoked
MyClass d;         // default constructor
d = c;             // assignment operator invoked

但是,我希望有人可以为这些提供类似的细分:

1) 对于第 2-3 行,是否调用了赋值运算符或复制构造函数?

MyClass arr[10];
arr[2] = a;
arr[5] = MyClass(1);

2) 构造函数,然后是复制构造函数?还是构造函数,然后是赋值运算符?

MyClass arr2[] = MyClass(), MyClass(9);

3) 假设向量v 的内部表示有空间容纳另一个对象,是使用赋值运算符还是复制构造函数复制新元素?

std::vector<MyClass> v;
v.push_back(MyClass(2));
...
...

4) 假设向量v 的内部表示空间不足且必须重新分配,是使用赋值运算符还是复制构造函数复制旧元素?

v.push_back(MyClass(2)); // Assuming vector is out of space; must realloc

【问题讨论】:

1) 通过在相关功能中打印诊断方法,您能找到自己吗? 2)这些答案是具体实现的吗? 1) 你完全错过了移动构建。 2) std::vector 如何调整大小取决于移动构造函数是否不抛出。 构造函数用于构造。赋值运算符用于修改已经构造好的对象 【参考方案1】:
MyClass arr[10];

MyClass 的构造函数被调用了 10 次,因为创建了 10 个 arr 对象。

arr[2] = a;

调用赋值运算符,将arr[2] 赋值给a

arr[5] = MyClass(1);

第一个带有参数1 的单参数构造函数被调用并创建了一个MyClass 的对象。然后调用赋值运算符。

MyClass arr2[] = MyClass(), MyClass(9);

这里只调用了两个构造函数。首先是Myclass(),然后是“单参数构造函数”MyClass(9)。带有初始化的数组声明不是赋值,因为不存在可以赋值的现有数组成员。

std::vector<MyClass> v;

std::vector&lt;MyClass&gt; 的构造函数被调用。

v.push_back(MyClass(2));

std::vector::push_back 创建类的副本并存储它。所以首先调用MyClass(2)构造函数,然后调用复制构造函数MyClass(const MyClass &amp;)来复制值。然后存储复制的对象。

假设向量 v 的内部表示空间不足并且必须重新分配,是使用赋值运算符还是复制构造函数复制旧元素?

为每个成员调用复制运算符。所以:

std::vector<MyClass> a;

std::vector&lt;MyClass&gt;调用构造函数

a.push_back(MyClass(1));

调用MyClass(1) 的构造函数并使用复制构造函数MyClass(MyClass&amp;) 复制它。 之后,如果我们向数组中添加另一个元素:

a.push_back(MyClass(2));

然后调用MyClass(2)构造函数,然后为刚刚构造的MyClass(2)对象调用MyClass(MyClass&amp;)的复制构造函数。然后向量复制构造来自 ned 的所有现有成员,因此对于向量MyClass(1) 中的预先存在的对象,调用已经向上的复制构造函数。

真的,玩一下。并在任何地方插入 cout 以查看它:

struct MyClass 
    MyClass(int a = 0) : _a(a) 
        _count = count++;
        std::cout << __func__ << " " 
            << _count << " "
            << this << " "
            << this->_a << "\n";
    
    MyClass(const MyClass &old_obj) 
        this->_a = old_obj._a;
        std::cout << __func__ << "Copy " 
            << _count << " "
            << this << " "
            << this->_a << " "
            << &old_obj << " "
            << old_obj._a << "\n";
    
    void operator=(const MyClass &old_obj) 
        this->_a = old_obj._a;
        std::cout << "MyClass" << __func__ << " " 
            << _count << " "
            << this << " "
            << this->_a << " "
            << &old_obj << " "
            << old_obj._a << "\n";
    
    static int count;
    int _count;
    int _a;
;

int MyClass::count = 0;

【讨论】:

【参考方案2】:

当你有

type variable_name = some_value

那么你声明了一个变量并且总是调用它的构造函数(如果有的话)。这称为复制初始化,永远不会被赋值

所以,在

MyClass arr[10];      // 1
arr[2] = a;           // 2
arr[5] = MyClass(1);  // 3

第 1 行创建一个包含 10 个 MyClass 的数组,并默认构造每个数组。第 2 行和第 3 行是赋值。

MyClass arr2[] = MyClass(), MyClass(9);

您使用 *braced-init-list` 中的值作为数组成员的初始化器来初始化 2 个对象的数组。管理list initialization 的规则有很多,但它们的共同点是不会发生赋值,只会发生构造函数调用。

std::vector<MyClass> v;
v.push_back(MyClass(2));

假设向量没有重新分配,您有一个构造函数调用MyClass(2),然后向量中的元素是从该临时对象复制构造的。如果向量必须增长,则将所有当前元素复制/移动构造到新缓冲区,然后在最后复制临时元素。

【讨论】:

以上是关于是否为数组/向量插入调用了赋值运算符或复制构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何从基类调用派生赋值运算符?

在复制构造函数中调用赋值运算符

C++ 数组对复制构造函数和赋值运算符

派生类在基类中删除时是不是会有隐式复制构造函数或赋值运算符?

c++ 类的复制函数

从复制构造函数调用默认赋值运算符是不好的形式吗?