C++ 构造函数触发析构函数
C++ A constructor triggers a destructor
我正在尝试使用向量和类制作一个Monster
生成器,以允许用户根据需要创建和删除Monster
对象。但是,当我在代码中调用Monster
的构造函数时,它也会立即触发析构函数,从而从向量容器中删除Monster
。
这是我的代码片段,它创建和销毁Monster
对象:
while (p == 't')
{
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
while (p == 'u' && Monster::n == 0)
{
cout << "No monsters to remove." << endl;
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
}
while (p != 'g' && p != 'u')
{
cout << "Error. Follow the instructions." << endl;
cout << "Generate (g) or remove (u) a monster?" << endl;
cin >> p;
}
if (p == 'g')
{
{
vec.push_back(Monster());
vec[Monster::n].print();
}
}
if (p == 'u')
{
vec.~Monster();
}
cout << "Continue (t) or finish (n)?" << endl;
cin >> p;
while (p != 't' && p != 'n')
{
cout << "Error. Follow the instructions?" << endl;
cout << "Continue (t) or finish (n)?" << endl;
cin >> p;
}
这是Monster
的构造函数:
Monster::Monster()
{
ostringstream buffer;
buffer << Monster::n;
string name = buffer.str();
Monster::setname(name);
Monster::settype(MonsterType(rand() % 7 + 1));
Monster::setattack(rand() % 25);
Monster::sethealth(rand() % 15);
cout << endl << "Monster generated." << endl << endl;
Monster::n++;
}
这是析构函数:
Monster::~Monster()
{
cout << "Monster removed." << endl << endl;
Monster::n--;
}
当我输入字符'g'
时,它会按预期创建一个Monster
对象,但随后立即销毁它,甚至在使用print()
方法打印其统计信息之前。这意味着我的代码没有按预期运行,因为它永远不会允许正确创建或销毁Monster
对象。
有人可以帮我了解这里发生了什么吗?
编辑:
好的,我能够通过制作另一个构造函数来解决这个问题。
就是这样:
Monster::Monster(const Monster & mon)
{
Monster::name = mon.name;
Monster::type = mon.type;
Monster::attack = mon.attack;
Monster::health = mon.health;
cout << endl << "Monster created (cpy)." << endl << endl;
Monster::n++;
}
我编辑了用户决定生成以下内容Monster
的点:
{
vec.push_back(Monster());
vector<Monster> veccpy = {};
veccpy.push_back(Monster(vec[Monster::n-1]));
vec.back().print();
}
当我打开程序时,它会吐出这样的东西:
创造的怪物
怪物创建(cpy)
怪物被移除
怪物创建(cpy)
怪物创建(cpy)
怪物被移除
怪物的统计数据
现在,奇怪的是,即使它如此奇怪地使用这些构造函数,该程序也按照我的预期工作。n
按预期迭代,我可以按预期删除或添加Monster
,并且Monster
统计信息将按预期显示。
有人可以指出我在这里做错了什么吗?
在这一行中:
vec.push_back(Monster());
您正在创建 Monster 类型的临时对象,该对象正在复制到vec
中。因为它是一个临时对象,所以它将在语句以分号结尾后立即销毁。复制到vec
后将被销毁。
vec.~Monster();
这肯定不会编译,如果你想清除你的向量,那就用vec.clear()
在编写代码时Monster::setname(name);
大多数程序员会认为你在设置一个静态变量。我希望您不要将monster
的名称保留为静态变量。
您需要在代码中修复的另一件事是确保它遵守三规则(就像在注释中一样)。您的Monster::n
变量是一种由 Monster 类管理的资源,要正确管理它,您需要在所有可能创建/破坏 Monster 类实例的构造函数和运算符中适当修改它。下面没有用长文本描述它,下面是一个示例代码,它显示了在您的代码中调用的构造函数/运算符:
http://coliru.stacked-crooked.com/a/9184f137b7a90995
struct Monster {
Monster() { std::cout << n << ":" << __PRETTY_FUNCTION__ << "n"; n++; }
~Monster() { std::cout << n << ":" << __PRETTY_FUNCTION__ << "n"; n--; }
Monster(const Monster&) { std::cout << n << ":" << __PRETTY_FUNCTION__ << "n"; n++; }
Monster& operator=(const Monster&) { std::cout << n << ":" << __PRETTY_FUNCTION__ << "n"; n++; return *this; }
static int n;
};
int Monster::n;
int main()
{
{
std::vector<Monster> vec;
std::cout << "-------------- STARTn";
vec.push_back(Monster());
std::cout << "-------------- ENDn";
}
std::cout << Monster::n; // should output 0
}
-------------- START
0:Monster::Monster() // temporary created
1:Monster::Monster(const Monster&) // vector element is copy constructed
// from temporary here you have missed Monster::n++
2:Monster::~Monster() // temporary monster destroyed
-------------- END
1:Monster::~Monster() // this is a vector being destroyed
0 // this should always be 0, otherwise
// still something is missing.
vec.push_back(Monster());
这将创建一个临时对象,一个Monster
类的实例,并将其传递给向量的push_back()
方法。
push_back()
将获取其参数并将其复制到向量中。由于向量拥有其对象,这将涉及构造Monster
类的另一个实例。
push_back()
返回它传递的临时对象,因为参数将被销毁,从而调用对象的析构函数。这就是为什么你在这里看到一个析构函数被调用。
请注意,向向矢量添加对象时,矢量可以随时选择重新分配其内容。这本质上将涉及在向量中复制构造类的新实例,然后销毁旧实例。
经验 教训
花一些时间阅读你的C++书,了解智能指针是如何工作的,并将智能指针放在你的向量中,而不是对象实例中,以避免由于临时而引起的不必要的副作用。
您还可以实现移动构造函数作为替代解决方案,但使用智能指针可能会更容易。
第二种选择是emplace_back()
,而不是push_back()
新实例。但是,这并不能完全解决您的问题。见上文"重新分配"。
此外,使您的班级符合 3 规则也不会有什么坏处。你问这个问题的真正根本原因可能是因为它目前不是。
你所看到的是意料之中的,它正在发生,因为你已经蝑视了vec
,就像std::vector<Monster>
一样。声明包含类对象的向量是什么意思Monster
。
现在矢量的工作方式是,它们需要使所有这些 Monster 类对象保持活动状态,除非您将它们从矢量中删除。这意味着,这些Monstors对象的范围应该是等效的,直到对象仍然是向量。
话虽如此,现在如果你看这行代码vec.push_back(Monster());
,你声明的新 Monster 对象帽子将具有范围,直到if
中的大括号范围,一旦它超出范围,它将无法访问。这就是为什么当push_back
被执行时,它会复制给定的 Monster 对象(通过执行其默认的复制构造函数,或自定义的构造函数,如果有的话),并为向量创建一个新对象。
如果您不想复制要复制的对象,请执行以下操作:
将向量声明为std::vector<std::unique_ptr<Monster>>
和 称push_back
为.push_back(make_unique<Monster>())
。
在这里,您不是在本地堆栈上声明 Monster,而是在堆上声明它,并使用智能指针来管理堆上对象的生命周期。
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 在c++中使用向量时,如何调用构造函数和析构函数
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 不命名构造函数和析构函数上的类型错误
- 在 UML 类图中为C++类添加构造函数和析构函数
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 为什么类构造函数为其成员生成析构函数?
- makefile对我的名称空间对象/函数/构造函数的不确定引用
- 没有可调用的匹配函数(构造函数),候选函数不同于对给定类型的引用
- C++库制作,隐藏在标头中没有友元的析构函数/构造函数
- 从另一个成员函数/构造函数调用C++构造函数是否执行初始值设定项列表
- 在另一个函数/构造函数 [C++] 中初始化后使变量只读
- std::调用函数时找不到函数构造函数
- 显式调用基类析构函数/构造函数合法吗
- 函数构造函数和nullptr
- 用委托构造函数构造函数
- 虚函数构造函数和调用虚函数时会发生什么
- 一些Qt函数/构造函数参数的内存管理
- 使用for_each或std::transform时,c++函函数构造函数是如何被调用的?
- std函数构造函数初始化