1

我刚刚开始std::variant在我的项目中使用。我有个疑问。std::variant下面显示的代码中的析构函数会做什么。Variant 保存void*数据。一旦变体超出范围,我认为它只会释放内存void*而不是指针指向的实际对象。所以在这种情况下会有内存泄漏。我想知道我的理解是否正确。

#include <iostream>
#include <memory>
#include <variant>
using namespace std;

class A {
    public:
    ~A(){
        cout<<"Destructor called"<<endl;
    }
};


int main() {
    std::variant<void*> data;
    A* b = new A();
    data = (void*)b;
    return 0;
}
4

2 回答 2

2

当变体析构函数触发时,它会为此时存储在变体中的任何类型的项目调用析构函数。如果那是void*,那么 C++ 会说“好吧,我会清理void*,因为这是一个原始类型,所以这是一个空操作。” 它不会看void*,意识到它实际上是一个指向 an 的指针A,然后delete把指针当成一个A*.

评论指出,使用 a 的变体是相当不寻常的void*。A 的void*意思是“我指的是某个东西,作为用户,你需要跟踪它是什么,并进行适当的投射和资源管理。” 变体意味着“我持有以下实际事物之一,我希望 C++ 记住哪一个并为我进行适当的资源管理。” 您可能需要重新考虑您的设计,因为可能有一种更简单的方法来做您打算在这里做的任何事情。

于 2019-11-18T20:42:56.053 回答
1

你是对的。标准库中唯一真正对指针执行delete(或delete[])操作的指针类是智能指针。

std::variant应该支持您持有任意数量的类型的一个对象,主要不是指向对象的指针。如果变体包含指针,则意味着其他一些对象拥有数据并负责删除它。

std::variant只能容纳一种类型的也很少有用。在这种情况下,您可以将该变量声明为该类型的普通变量。

这是一个使用std::variant能够保存两种不相关类型的对象的示例,并且破坏将按预期发生。

#include <iostream>
#include <variant>

class A {
public:
    ~A() { std::cout << "A destructor called\n"; }
};

class B {
public:
    B() {}
    B(const B&) = default;
    B& operator=(const B&) = default;
    ~B() { std::cout << "B destructor called\n"; }
};

int main() {
    std::variant<A, B> data; // now holds a default constructed A
    data = B();              // deletes the A and now holds a default constructed B
    std::cout << "---\n";
}

输出:

A destructor called  // in "data = B()", the A must be destroyed
B destructor called  // the temporary B used in "data = B()"
---
B destructor called  // the B in the variant when the variant goes out of scope
于 2019-11-18T20:30:48.187 回答