簡體   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