如果std :: variant包含void *数据,析构函数将如何处理

问题描述 投票:1回答:2

我刚刚在我的项目中开始使用std::variant。我有个疑问。 std::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;
}
c++ memory-leaks c++17 void-pointers std-variant
2个回答
2
投票

当变体析构函数触发时,它将针对该点上变体中存储的任何类型的项目调用析构函数。如果那是void*,那么C ++会说“好吧,我将清理void*,由于那是原始类型,所以没有操作。”它不会看void*,要意识到它实际上是指向A的指针,然后是delete就像它是A*的指针。

评论指出,使用void*的变体是很不常见的。 void*的意思是“我要指的是什么,这取决于用户,您要跟踪它是什么,并进行适当的投射和资源管理。”一个变体表示“我拥有以下内容之一实际上,我希望C ++记住哪一个,并为我做适当的资源管理。”您可能想重新考虑设计,因为可能有一种更简便的方法来完成您打算在这里进行的工作。


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
© www.soinside.com 2019 - 2024. All rights reserved.