繁体   English   中英

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

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

  1. 为什么 ~Base() 在调用 emplace_back() 后立即被调用
  2. 为什么在析构函数调用后可以访问 sayHello()
  3. 为什么 ~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;
}

程序输出...

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

在调用vBase.emplace_back ( Base() ); 您首先创建一个Base对象。 该向量在原地创建另一个Base ,然后将第一个Base拥有的资源移动到新的Base中。 然后删除第一个碱基。 在您的向量中,您现在有一个移动的构造Base ,这就是调用sayHello()起作用的原因。

您可能想要做的是让emplace_back实际构造对象,而无需手动创建临时对象。 您只需提供构建Base所需的参数即可做到这一点。 像这样:

vBase.emplace_back();

你错过了安位点。 Emplace 函数从给定的参数就地构造对象,而不是例如push_back从预先存在的对象复制构造它。 您应该已经编写了vBase.emplace_back() ,它在没有构造函数参数的情况下就地构造向量内部的对象(即默认构造)。

就目前而言,您实际上默认通过Base()构造一个Base对象,将其传递给 emplace,它调用构造函数获取Base对象(即移动构造函数),复制它,然后复制原始对象,即Base()对象被摧毁。

它的副本仍在向量中,这就是您仍然可以访问它的原因。 被破坏的是临时的。 第二个析构函数调用是当向量超出范围时被销毁的副本。

所以你基本上只是在做与push_back相同的事情。

您没有在计数器中包含由移动和复制构造函数创建的对象,也没有记录调用。 如果您对日志进行更改以修复违反三/五规则的规则,您将看到以下内容:

#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;
}

输出:

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

住在 Coliru

暂无
暂无

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

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