【问题标题】:Debug assertion failed on class destructor类析构函数上的调试断言失败
【发布时间】:2016-12-29 23:25:58
【问题描述】:

我在 c++ 中做一个 dll,第二次调用函数时出现此错误:

调试断言失败表达式:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

我做了一些研究,这似乎发生在我 delete 一个我没有 new 的对象时

这是我认为失败的代码

class tFont
{
public:
    tFont(char chars[], __int64 cods[], int count);
    ~tFont();


    int fontCount = 0;
    char* characters = NULL;
    long long* codes = NULL;

    std::vector<std::pair<char,long long>> exceptions;


    char getCharacter(long long code);


};

tFont::tFont(char chars[], long long cods[], int count)
{
    characters = new char[count];
    codes = new long long[count];
    fontCount = count;

    for (int i = 0; i < count; i++)
    {
        characters[i] = chars[i];
        codes[i] = cods[i];
    }


}


tFont::~tFont()
{

        delete[] characters;
        delete[] codes;
}

Visual Studio 在出错后停止在delete[] characters;

我尝试了这个没有任何运气

if (characters != NULL)
{
    delete[] characters;
}
if (codes != NULL)
{
    delete[] codes;
}

我只创建这个类的一个实例作为静态对象

tFont* getCapFont()
{
    static tFont *capFont = NULL;

    if (capFont == NULL)
    {
        char characters[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
        long long codes[] = { 241136, 441, 183861, 102321, 45836, 5955, 19305, 7871, 220321, 102286 };
        int fontCount = 10;

        capFont = new tFont(characters, codes, fontCount);
        pair<char, long long> e;
        e.first = '0';
        e.second = 24045;

        capFont->exceptions.push_back(e);
    }

    return capFont;
} 

为什么即使我从不调用析构函数delete capFont

谢谢!

Edit2:我按照'jarmod'所说的那样做,抛出错误的那一刻是这个函数结束的时候

void analysis::singleLineTextReader(tImage img, char result[], tFont font)

我说这是我第二次调用函数时发生的,可能singleLineTextReader结束时调用了其参数font的析构函数?上面描述的静态对象是哪个

【问题讨论】:

  • 只是猜测,但请参阅Rule of 3/5/0。我还建议查看std::vectorstd::unique_ptrstd::shared_ptr
  • 将一些调试打印语句添加到各种方法中,看看发生了什么。可能不是你想的那样。此外,在调用 delete 之前没有绝对需要测试 NULL,但是在调用 delete 之后将指针设为 null 是一个很好的做法(并且可以选择在删除之前断言它不是 null,以捕获您不小心出现的编码错误双删除指针)。
  • Edit2 后:singleLineTextReader 获取其参数按值,这意味着它创建了字体的副本。该副本将包含在构造函数中创建的指针的副本,这些将在析构函数中被删除。之后,原始字体对象将包含悬空指针。
  • “我删除了一个我没有新建的对象”,你为什么要这样做?
  • @BoPersson - Captain Obvlious 啊,当我通过复制传递参数时,它会自动调用类的复制构造函数,谢谢!

标签: c++


【解决方案1】:
void analysis::singleLineTextReader(tImage img, char result[], tFont font)

注意font 是按值传递的。您没有将指针传递给它。您没有传递对它的引用。你传递它的

所以你正在传递singleLineTextReader 一个 tFont 对象,该对象是通过复制构造你传递给它的对象而创建的。当副本的析构函数运行时,它会破坏底层对象的成员。当你再次这样做时,你正在破坏已经被破坏的对象。

遵循 3/5/0 的规则。不要在不需要时复制对象。

除非你别无选择,否则不要使用new/new[]。在这里你可以使用各种其他的东西,比如矢量。

【讨论】:

  • 谢谢!这就是我从现在开始使用更多指针的解决方案
  • 正确的教训是更少指针,尤其是。拥有原始指针是一个 nono
  • (facepalm at "I will use more pointers") 改用智能指针。如果您只更改 char* characters = NULL; 而不是编写 std::unique_ptr&lt;char[]&gt; characters; (默认构造为 NULL 隐式)并摆脱 delete [] characters; ,那么(1)将不再可能意外复制,并且(2)您缺乏由于天真的析构函数逻辑导致的异常安全性完全消失了。如果您类似地编写std::unique_ptr&lt;long long[]&gt; codes;,那么编译器将完全自动为您编写析构函数。
猜你喜欢
  • 2014-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多