free():在 C 中的 tcache 2 中检测到双重空闲

新手上路,请多包涵

首先,我真的检查了是否有问题已经被问过,但我找不到任何问题。错误消息不应该欺骗你我猜我的情况有点不同,或者我只是错过了一些东西。

当我处理一个玩具 C++ 代码时,我遇到了一个奇怪的错误。程序输出说有双重释放的情况,但我看不到这个错误发生的地方。代码可能有点长,对此我深表歉意。

我现在正在研究 Linux Distribution 并且我正在使用 g++ 9.1.0 。我检查了我的代码并寻找错误的部分。

即使我修复了部分代码,我的问题并没有得到解决,除非我评论 Foo{1, "Hello World"};vec.push_back(std::move(Foo{})); 我不明白为什么。

 class Foo
{
public:
    Foo()
        : val{nullptr}, str{nullptr}
    {
        std::cout << "You are in empty constructor\n";
    }

    Foo(int the_val, const char *the_str)
        : val{new int}, str{new char[std::strlen(the_str + 1)]}
    {
        *val = the_val;
        std::cout << *val << '\n';
        std::strcpy(str, the_str);
        std::cout << str << '\n';
    }

    ~Foo()
    {
        if (val) {
            delete val;
        } else {
            std::cout << "val is empty\n";
        }

        if (str) {
            delete[] str;
        } else {
            std::cout << "str is empty\n";
        }
    }

    Foo(const Foo&) = delete;
    Foo& operator= (const Foo&) = delete;

    Foo(Foo&& rhs)
    {
        std::cout << "Move constructor is triggered\n";

        if (val) {
            delete val;
        }
        val = rhs.val;
        rhs.val = nullptr;

        if (str) {
            delete[] str;
        }
        str = rhs.str;
        rhs.str = nullptr;
    }

    Foo& operator= (Foo& rhs)
    {
        std::cout << "Move assignment is triggered\n";

        // Self-assignment detection
        if (&rhs == this) {
            return *this;
        }

        if (val) {
            delete val;
        }
        val = rhs.val;
        rhs.val = nullptr;

        if (str) {
            delete[] str;
        }
        str = rhs.str;
        rhs.str = nullptr;

        return *this;
    }
private:
    int *val;
    char *str;
};

int main()
{
    Foo{1, "Hello World"};

    std::vector<Foo> vec;
    vec.push_back(std::move(Foo{}));

    return 0;
}

如果我不在函数 main 中的任何地方注释,输出如下。

 1
Hello World
You are in empty constructor
val is empty
str is empty
You are in empty constructor
Move constructor is triggered
free(): double free detected in tcache 2
Aborted (core dumped)

如果我评论 “Foo{1, “Hello World”};“,输出变为

You are in empty constructor
Move constructor is triggered
val is empty
str is empty
val is empty
str is empty

最后,当我评论“vec.push_back(std::move(Foo{}));”时,输出变为

You are in empty constructor
Move constructor is triggered
val is empty
str is empty
val is empty
str is empty

原文由 eminomur 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 860
1 个回答

首先,这个构造函数使用了错误的内存初始化器

Foo(int the_val, const char *the_str)
    : val{new int}, str{new char[std::strlen(the_str + 1)]}
                                             ^^^^^^^^^^^

我想你的意思是

Foo(int the_val, const char *the_str)
    : val{new int}, str{new char[std::strlen(the_str  ) + 1]}

此移动构造函数也无效

Foo(Foo&& rhs)
{
    std::cout << "Move constructor is triggered\n";

    if (val) {
        delete val;
    }
    val = rhs.val;
    rhs.val = nullptr;

    if (str) {
        delete[] str;
    }
    str = rhs.str;
    rhs.str = nullptr;
}

在构造函数的主体中,数据成员 valstr 具有不确定的值。当构造函数的主体获得控制权时,它们没有被初始化。

你可以这样写

Foo(Foo&& rhs) : val( nullptr ), str( nullptr )
{
    std::cout << "Move constructor is triggered\n";

    std::swap( val, rhs.val );
    std::swap( str, rhs.str );
}

该运算符

Foo& operator= (Foo& rhs)

不是移动赋值运算符。它是一个复制赋值运算符。所以它的定义是不正确的。

这也是主要的声明

Foo{1, "Hello World"};

没有意义。对象被创建并立即被删除。

在这份声明中

vec.push_back(std::move(Foo{}));

std::move 是多余的,因为 Foo{} 已经是一个右值。

原文由 Vlad from Moscow 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题