【问题标题】:What will destructor of a std::variant do if it contains void* data如果 std::variant 包含 void* 数据,它的析构函数会做什么
【发布时间】:2019-11-18 19:49:19
【问题描述】:

我刚刚开始在我的项目中使用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;
}

【问题讨论】:

  • 是的,你的理解是正确的。
  • 我认为在std::variant 中持有void* 没有任何意义。如果您实际使用它,那么您使用std::variant 的方式可能有问题。在std::variant 中持有指针似乎已经不寻常了。你为什么要问void* 而不是A*?可以肯定的是,如果变体持有A*A 对象也不会被销毁。
  • 一个小修正(很抱歉吹毛求疵):析构函数本身不会释放void * 占用的内存,因为它没有存储在堆中。 void * 存储在变体的成员变量中(一种或另一种方式),因此当变体占用的内存被释放时,它所占用的内存也被释放。
  • 这和void*有什么关系?任何(原始)指针的行为方式都相同。
  • @DavisHerring 是的,你是对的。

标签: c++ memory-leaks c++17 void-pointers std-variant


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    你是对的。标准库中唯一真正对指针执行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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-26
      • 2014-12-28
      • 1970-01-01
      • 2012-05-10
      • 2011-10-10
      • 2015-02-04
      • 2012-07-24
      相关资源
      最近更新 更多