#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
答案 0 :(得分:9)
您错过了重点。 Emplace函数从给定的参数就地构造对象,而不是例如push_back
从先前存在的对象复制构造它。您应该已经编写了vBase.emplace_back()
,它可以在向量内部就位构造对象,而没有构造函数参数(即默认构造)。
就目前情况而言,您实际上是通过Base
默认构造一个Base()
对象,并将其传递给emplace,这会调用带有Base
对象的构造函数(即move构造函数),复制它,然后破坏原始对象Base()
。
其副本仍在引导程序中,这就是为什么您仍然可以访问它的原因。被摧毁的是临时的。第二个析构函数调用是当向量超出范围时销毁副本。
因此,您基本上只是在做与push_back
相同的事情。
答案 1 :(得分:9)
在调用vBase.emplace_back ( Base() );
中,您首先创建一个Base
对象。该向量在适当的位置创建了另一个Base
,第一个Base
拥有的资源随后被移到新的资源中。然后删除第一个碱基。在向量中,您现在有了一个移动的构造Base
,这就是对sayHello()
的调用起作用的原因。
您可能想要做的是让emplace_back
实际构造对象,而无需手动创建临时对象。通过仅提供构造Base
所需的参数来做到这一点。像这样:
vBase.emplace_back();
答案 2 :(得分:8)
您不会在计数器中包括由move和copy构造函数创建的对象,也不会记录调用。如果您更改日志记录以解决违反“三五规则”的情况,则会看到以下内容:
#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)