简体   繁体   English

为什么在此示例中使用 std::vector 调用复制构造函数?

[英]Why is the copy constructor called in this example with std::vector?

#include <iostream>
#include <vector>
using namespace std;

// Move Class
class Move {
private:
    // Declare the raw pointer as
    // the data member of class
    int* data;

public:

    // Constructor
    Move(int d)
    {
        // Declare object in the heap
        data = new int;
        *data = d;
        cout << "Constructor is called for "
            << d << endl;
    };

    // Copy Constructor
    Move(const Move& source)
        : Move{ *source.data }
    {
        cout << "Copy Constructor is called -"
            << "Deep copy for "
            << *source.data
            << endl;
    }

    // Move Constructor
    Move(Move&& source)
        : data{ source.data }
    {

        cout << "Move Constructor for "
            << *source.data << endl;
        source.data = nullptr;
    }

    // Destructor
    ~Move()
    {
        if (data != nullptr)
            cout << "Destructor is called for "
                << *data << endl;
        else
            cout << "Destructor is called"
                << " for nullptr "
                << endl;
        delete data;
    }
};

// Driver Code
int main()
{
    // Vector of Move Class
    vector<Move> vec;

    // Inserting Object of Move Class
    vec.push_back(Move{ 10 });
    vec.push_back(Move{ 20 });
    return 0;
}

output: output:
Constructor is called for 10构造函数被调用 10
Move Constructor for 10移动构造函数 10
Destructor is called for nullptr为 nullptr 调用析构函数

Constructor is called for 20构造函数被调用 20
Move Constructor for 20移动构造函数 20
Constructor is called for 10构造函数被调用 10
Copy Constructor is called -Deep copy for 10复制构造函数称为 -Deep copy for 10
Destructor is called for 10析构函数被调用 10
Destructor is called for nullptr为 nullptr 调用析构函数
Destructor is called for 10析构函数被调用 10
Destructor is called for 20析构函数被调用 20

I think what you are asking is why is the copy constructor called instead of your move constructor?我想你问的是为什么调用复制构造函数而不是你的移动构造函数? It is because your move constructor is not noexcept.这是因为你的移动构造函数不是 noexcept。 If you mark it as no except, the move constructor will be used during reallocation instead.如果将其标记为 no except,则将在重新分配期间使用移动构造函数。

See this related answer for more information: https://stackoverflow.com/a/47017784/6324364有关详细信息,请参阅此相关答案: https://stackoverflow.com/a/47017784/6324364

The common recommendation is that move constructors should be noexcept if at all possible.常见的建议是,如果可能的话,移动构造函数应该是 noexcept 。 Many standard library functions will not use the move constructor if it could throw an exception.如果移动构造函数可能引发异常,许多标准库函数将不会使用它。

If your question is instead about the order of constructor calls - that is just dependent on the implementation.如果您的问题是关于构造函数调用的顺序 - 那只取决于实现。 An example implementation of push_back could be: push_back的一个示例实现可以是:

// The callsite of this function is where "Constructor is called for 20"
template<typename T>
void push_back(T&& value) {
   // If the vector is full resize
   if (size() == capacity()) {
      auto* oldBuffer = m_buffer;
      // allocate larger amount of space,
      // increasing capacity but keeping size the same
      // ...

      // Here is your move constructor call
      m_buffer[size()] = std::move(value);

      // copy/move over old elements. This is your copy constructor
      // If your move constructor was noexcept, it would be used.
      // Otherwise the copy constructor is called.
      // ...

      m_size++;
      
      // deallocate old buffer
      // ...
   } else {
      m_buffer[size() - 1] = std::move(value);
      m_size++;
   }
}

The reason you seemingly get two constructor calls for 10 after the move constructor for 20 is because that is what your code does:20的移动构造函数之后,你似乎得到两个构造函数调用10的原因是因为这就是你的代码所做的:

// Constructor
    Move(int d)
    {
        // Declare object in the heap
        data = new int;
        *data = d;
        cout << "Constructor is called for "
            << d << endl;
    };

    // Copy Constructor
    Move(const Move& source)
        // Here, your Copy constructor is calling the other constructor
        : Move{ *source.data }
    {
        cout << "Copy Constructor is called -"
            << "Deep copy for "
            << *source.data
            << endl;
    }

In your copy constructor you are explicitly calling your other constructor leading to the two log messages.在您的复制构造函数中,您显式调用了导致两条日志消息的其他构造函数。

The implementation may do things in this order (move/copy new element, then move/copy old elements) as part of the strong exception guarantee provided by vector which says that the vector state will not be corrupted if copy/move of the new items fails.作为 vector 提供的强异常保证的一部分,该实现可能会按此顺序执行操作(移动/复制新元素,然后移动/复制旧元素),该保证表示如果复制/移动新项目,向量 state 将不会损坏失败。 If it moved the old items first and then the new item constructor threw an exception, the vector state would be corrupted.如果它先移动旧项,然后新项构造函数抛出异常,则矢量 state 将被破坏。

If you can add what you expected to happen in the code, maybe someone can give a better answer.如果您可以在代码中添加您期望发生的事情,也许有人可以给出更好的答案。 As it stands, I don't fully understand what you are asking.就目前而言,我不完全明白你在问什么。 You get those calls because that is how it is implemented.您会收到这些电话,因为这就是它的实施方式。

Vector is stored as a one piece in memory, continously. vector被连续存储在memory中。 When you add the second element to the vector, its current memory needs to be expanded to accomodate the newly added element.当您将第二个元素添加到向量时,其当前的 memory 需要扩展以容纳新添加的元素。 This will require your already existing elements to be copied somewhere else where enough memory is allocated to accomodate both (in your case) the first and second element.这将需要将您已经存在的元素复制到其他地方,其中分配了足够的 memory 以容纳(在您的情况下)第一个和第二个元素。 Take a look at what reserve is doing and how you can benefit from it.看看reserve正在做什么以及如何从中受益。 Or, similarly, if your vector is of fixed size, you may also take a look at array .或者,类似地,如果你的矢量是固定大小的,你也可以看看array

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM