简体   繁体   English

为什么 emplace_back() 表现得像这样?

[英]Why is emplace_back() behaving like this?

  1. Why does ~Base() get called immediately after the call to emplace_back()为什么 ~Base() 在调用 emplace_back() 后立即被调用
  2. Why is sayHello() accessible after the destructor call为什么在析构函数调用后可以访问 sayHello()
  3. Why does ~Base() get called again为什么 ~Base() 再次被调用
#include <iostream>
#include <vector>

class Base
{
    private:

        static int m_count;

    public:

        Base()
        {
            std::cout << " Base created. Count = " << ++m_count << std::endl;
        }

        ~Base()
        {
            std::cout << " Base destroyed. Count = " << --m_count << std::endl;
        }

        void sayHello() const
        {
            std::cout << " Base says hello" << std::endl;
        }
};

int Base::m_count = 0;

int main()
{
    {
        std::vector< Base > vBase;

        vBase.emplace_back ( Base() );  // <- Why does ~Base() get called here

        vBase[0].sayHello(); // <- Why is this function accessible after call to dtor
    }
    return 0;
}

Program output...程序输出...

Base created. Count = 1  
Base destroyed. Count = 0  
Base says hello
Base destroyed. Count = -1

In the call vBase.emplace_back ( Base() );在调用vBase.emplace_back ( Base() ); you first create a Base object.您首先创建一个Base对象。 The vector creates another Base in place and the resources owned by the first Base are then moved into the new one.该向量在原地创建另一个Base ,然后将第一个Base拥有的资源移动到新的Base中。 The first base is then deleted.然后删除第一个碱基。 Inside your vector you now have a moved constructed Base which is why the call to sayHello() works.在您的向量中,您现在有一个移动的构造Base ,这就是调用sayHello()起作用的原因。

What you probably want to do is to let emplace_back actually construct the object, in place without creating a temporary object manually.您可能想要做的是让emplace_back实际构造对象,而无需手动创建临时对象。 You do that by only supplying the arguments needed to construct a Base .您只需提供构建Base所需的参数即可做到这一点。 Like so:像这样:

vBase.emplace_back();

You're missing the point of emplace.你错过了安位点。 Emplace functions construct the object in-place from the given arguments, as opposed to eg push_back which copy-constructs it from a pre-existing object. Emplace 函数从给定的参数就地构造对象,而不是例如push_back从预先存在的对象复制构造它。 You should have written vBase.emplace_back() which constructs the object inside the vector in-place with no constructor arguments (ie default constructed).您应该已经编写了vBase.emplace_back() ,它在没有构造函数参数的情况下就地构造向量内部的对象(即默认构造)。

As it stands you're effectively default constructing a Base object via Base() , passing it to emplace, which calls the constructor taking a Base object (ie the move constructor), copying it, and then the original ie the Base() object is destroyed.就目前而言,您实际上默认通过Base()构造一个Base对象,将其传递给 emplace,它调用构造函数获取Base对象(即移动构造函数),复制它,然后复制原始对象,即Base()对象被摧毁。

Its copy is still in the vector, which is why you can still access it.它的副本仍在向量中,这就是您仍然可以访问它的原因。 What was destroyed was the temporary.被破坏的是临时的。 The second destructor call is the copy being destroyed when the vector goes out of scope.第二个析构函数调用是当向量超出范围时被销毁的副本。

So you're basically just doing the same thing as push_back .所以你基本上只是在做与push_back相同的事情。

You're not including the objects created by move and copy constructors in your counter, nor are you logging the calls.您没有在计数器中包含由移动和复制构造函数创建的对象,也没有记录调用。 If you make changes to your logging to fix the Rule of Three/Five violation, you'll see this:如果您对日志进行更改以修复违反三/五规则的规则,您将看到以下内容:

#include <typeinfo>
#include <iostream>

/// noisy
///
/// A class logs all of the calls to Big Five and the default constructor
/// The name of the template parameter, received as if by call
/// to `typeid(T).name()`, is displayed in the logs.
template<typename T>
struct noisy
{
    noisy& operator=(noisy&& other) noexcept { std::cout << "MOVE ASSIGNMENT<" << typeid(T).name() << ">(this = " << this << ", other = " << &other << ")\n"; return *this; }
    noisy& operator=(const noisy& other) { std::cout << "COPY ASSIGNMENT<" << typeid(T).name() << ">(this = " << this << ", other = " << &other << ")\n"; return *this; }
    noisy(const noisy& other) { std::cout << "COPY CONSTRUCTOR<" << typeid(T).name() << ">(this = " << this << ", other = " << &other << ")\n"; }
    noisy(noisy&& other) noexcept { std::cout << "MOVE CONSTRUCTOR<" << typeid(T).name() << ">(this = " << this << ", other = " << &other << ")\n"; }
    ~noisy() { std::cout << "DESTRUCTOR<" << typeid(T).name() << ">(this = " << this << ")\n"; }
    noisy() { std::cout << "CONSTRUCTOR<" << typeid(T).name() << ">(this = " << this << ")\n"; }
};

#include <iostream>
#include <vector>

class Base : public noisy<Base>
{
    public:

        void sayHello() const
        {
            std::cout << "Base says hello" << "(this = " << this << ")" << std::endl;
        }
};

int main()
{
    {
        std::vector< Base > vBase;

        vBase.emplace_back ( Base() );  // <- Why does ~Base() get called here

        vBase[0].sayHello(); // <- Why is this function accessible after call to dtor
    }
    return 0;
}

Output:输出:

CONSTRUCTOR<4Base>(this = 0x7fff300b580f)
MOVE CONSTRUCTOR<4Base>(this = 0x18a6c30, other = 0x7fff300b580f)
DESTRUCTOR<4Base>(this = 0x7fff300b580f)
Base says hello(this = 0x18a6c30)
DESTRUCTOR<4Base>(this = 0x18a6c30)

Live on Coliru住在 Coliru

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

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