我有Deck和PlayingCard课程。 Deck对象必须具有动态分配的指向PlayingCard对象的指针数组:
PlayingCard** _playing_cards;
要初始化此数组,将调用Deck的构造函数和build()函数:
Deck::Deck(int size)
{
_total_playing_cards = size;
_deal_next = 0;
build();
}
void Deck::build()
{
_playing_cards = new PlayingCard*[_total_playing_cards];
for(int i = 1; i <= _total_playing_cards; ++i)
{
_playing_cards[i-1] = new PlayingCard(i % 13, i % 4);
}
}
释放使用'new'分配的内存在析构函数中处理:
Deck::~Deck()
{
for(int i = 0; i < _total_playing_cards; ++i)
{
delete[] _playing_cards[i];
}
delete[] _playing_cards;
}
然后我有一个单独的文件,deck_test.cpp,它有一个main()来简单地构造和破坏Deck对象:
int main()
{
Deck deck(52);
deck.~Deck();
return 0;
}
这编译很好,但在调试时,Visual Studio在Play Cards.exe中报告“0x5ab159da(msvcr100d.dll)的未处理异常:0xC0000005:访问冲突读取位置0xfeeefee2。”在查看调用堆栈时,问题似乎发生在我在析构函数中的'for'循环中使用'delete []'运算符的地方。这不是从指针数组中释放内存的正确方法吗?
答案 0 :(得分:2)
你的Deck析构函数需要阅读如下:
Deck::~Deck()
{
for(int i = 0; i < _total_playing_cards; ++i)
{
delete _playing_cards[i];
}
delete[] _playing_cards;
}
请注意,在循环中,您必须使用非数组删除来删除单个扑克牌。
还有另一个更大的问题,即你正在调用析构函数两次 - 一次是在你的显式调用中,第二次是当deck在main()
结束时超出范围。基本上你不应该在C ++中手动调用非堆分配对象上的析构函数,因为你干扰了C ++对象的内置生命周期管理。不好的想法,除非你(a)真的知道你在做什么,(b)你是在非常具体的情况下做的。
顺便说一句,除非你正在学习指针并尝试使用指针,否则使用动态分配的指针数组会带来所有开销。在生产代码中,请自己和其他人一个忙,并使用std :: vector代替。
答案 1 :(得分:1)
请不要直接在main()中调用析构函数。
稍微修改析构函数代码:
Deck::~Deck()
{
if (_playing_cards) {
for (std::size_t i = 0; i < _total_playing_cards; ++i) {
delete _playing_cards[i];
_playing_cards[i] = NULL;
}
delete[] _playing_cards;
_playing_cards = NULL;
}
}
顺便说一下,为什么不使用std::vector<PlayingCard>
?
答案 2 :(得分:0)
这是因为两件事:
手动调用析构函数,然后在变量超出范围时再次调用析构函数。你通常不应该手动调用析构函数,除非对象是使用展示位置new
分配的,或者在它超出范围之前偷偷重建具有展示位置new
的对象(但不要这样做)
delete[] _playing_cards[i];
应为delete _playing_cards[i]
,因为playing_cards[i]
不是数组,只是new PlayingCard
。
另外,为什么你在一个地方i = 1; i <= _total_playing_cards
和另一个地方i = 0; i < _total_playing_cards
?它不必要地使事情变得复杂,我建议选择一个(最好是后者)并坚持下去。
答案 3 :(得分:0)
你不必打电话给甲板.~甲板();靠自己。它会自动调用。只需使用:
int main()
{
Deck deck(52);
return 0;
}
并在for循环中使用delete _playing_cards[i];
,因为delete[]
表示删除数组,而delete
表示只删除一个元素。