【问题标题】:Releasing memory for array of pointers?释放指针数组的内存?
【发布时间】:2012-02-04 19:35:50
【问题描述】:

我有甲板和扑克牌课程。 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 会报告“Playing Cards.exe 中 0x5ab159da (msvcr100d.dll) 处的未处理异常:0xC0000005:读取位置 0xfeeefee2 的访问冲突。”查看调用堆栈时,问题似乎发生在我在析构函数的“for”循环中使用“delete[]”运算符的地方。这不是从指针数组中释放内存的正确方法吗?

【问题讨论】:

  • 在 for 循环中,不带 [] 的删除是否足够,因为您正在删除 _playing_cards[i] 指向的空间和对象?

标签: arrays pointers visual-c++ dynamic delete-operator


【解决方案1】:

您的 Deck 析构函数需要如下所示:

Deck::~Deck()
{
    for(int i = 0; i < _total_playing_cards; ++i)
    {
        delete _playing_cards[i];
    }
    delete[] _playing_cards;
}

请注意,在循环中,您必须使用非数组删除来删除单个扑克牌。

还有一个更大的问题,即你调用了析构函数两次——一次是在你的显式调用中,第二次是在main() 末尾的deck 超出范围时。基本上,您永远不应该在 C++ 中手动调用非堆分配对象的析构函数,因为您正在干扰 C++ 对象的内置生命周期管理。除非你 (a) 真的知道你在做什么,并且 (b) 你是在非常特定的情况下做的,否则这是个坏主意。

顺便说一句,使用动态分配的指针数组会带来所有开销,这是一个坏主意,除非您正在学习指针并尝试使用它们。在生产代码中,请帮自己和其他人一个忙,改用 std::vector。

【讨论】:

  • 同意向量的使用,但这是一个学术项目,所以我的双手被束缚在我可以使用和不能使用的问题上。
  • 因此评论“除非你正在学习指针”:)。在我看来,了解它们是个好主意,但在现代 C++ 代码中,它们的合法用途远比人们想象的要少。
【解决方案2】:

请不要在 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&lt;PlayingCard&gt;

【讨论】:

  • 这无济于事,您只会在第二次通过析构函数时解除对 NULL 的引用。
  • 您也不需要将_playing_cards[i] 设置为NULL
【解决方案3】:

你不必调用deck.~Deck();自己。它将被自动调用。只需使用:

int main()
{
    Deck deck(52);
    return 0;
}

并在for循环中使用delete _playing_cards[i];delete[]表示删除一个数组,delete表示只删除一个元素。

【讨论】:

    【解决方案4】:

    这是因为两件事:

    1. 您手动调用析构函数,然后当变量超出范围时再次调用析构函数。您通常不应该手动调用析构函数,除非该对象被分配了位置new,或者您在对象超出范围之前偷偷地用位置new 重构了该对象(但不要这样做)。

    2. delete[] _playing_cards[i]; 应该是delete _playing_cards[i],因为playing_cards[i] 不是一个数组,它只是一个new PlayingCard

    另外,你为什么在一个地方做i = 1; i &lt;= _total_playing_cards而在另一个地方做i = 0; i &lt; _total_playing_cards?它不必要地使事情复杂化,我建议选择一个(最好是后者)并坚持下去。

    【讨论】:

      猜你喜欢
      • 2016-07-18
      • 1970-01-01
      • 2013-12-30
      • 1970-01-01
      • 2020-06-23
      • 2022-09-27
      • 1970-01-01
      • 2014-02-15
      • 1970-01-01
      相关资源
      最近更新 更多