是否可以为 void* 创建 shared_ptr?

is it possible to create shared_ptr for void*?

我有 class 可以包含指向某些数据和他的数据类型的指针。 所以在每时每刻我都可以使用转换为正确类型来处理这些数据。

这里是 int 和 float 的例子:

enum MyType {
    NO_TYPE,
    INT,
    FLO
};

class MyClass {
public:
    MyType type;
    void* data;

    MyClass(int i)
        :
            type(MyType::INT)
    {
        data = (void*) new int(i);
    }

    MyClass(float i)
        :
            type(MyType::FLO)
    {
        std::cout << "Constructor\n";
        data = (void*) new float(i);
    }

    MyClass()
        :
            type(MyType::NO_TYPE)
    {
        std::cout << "Constructor (default)\n";
        data = nullptr;
    }

    void Copy(const MyClass &from)
    {
        this->type = from.type;
        if (this->type == MyType::INT)
            this->data = (void*) new int (*((int*)from.data));
        if (this->type == MyType::FLO)
            this->data = (void*) new float (*((float*)from.data));
    }

    MyClass(MyClass &from) {
        std::cout << "Copy constructor\n";
        Copy((const MyClass&)from);
    }

    MyClass(const MyClass &from) {
        std::cout << "Copy constructor\n";
        Copy(from);
    }

    ~MyClass() {
        std::cout << "Destructor for type "  << this->type << "\n";
        if (this->type == MyType::INT)
            delete (int*)this->data;
        if (this->type == MyType::FLO)
            delete (float*)this->data;
        this->data = nullptr;
    }
};

我想用shared_ptr重写它。但我的主要问题 datavoid*。有什么技巧可以帮到我吗?

更新: 写这个 class 的主要目的是在一个队列中存储一些不同的数据 喜欢 queue<MyClass>

未知数太多,无法单方面回答。

  • 如果你知道编译前的类型,你可以将这个class转换成一个模板。
  • 如果你想要类型安全,你可以使用类型安全联合 - std::variant
  • 您可以创建一个工厂或访问者 class 来为每个存储class分配和分离 classes
  • 如果您需要将数据存储在整体数组中,例如使用一些外部的、非 C++ API,例如OpenGL,你必须切换到 unsigned char*

Is there is some techniques that may helps me?

你当然可以做到。这是您的 class 的更新版本,其中包含一些测试代码。

#include <iostream>
#include <memory>

enum MyType {
   NO_TYPE,
   INT,
   FLO
};

class MyClass {
   public:
      MyType type;
      std::shared_ptr<void> data;

      static void int_deleter(int* ptr)
      {
         std::cout << "Deleting an int*\n";
         delete ptr; 
      }

      static void float_deleter(float* ptr)
      {
         std::cout << "Deleting a float*\n";
         delete ptr;
      }

      MyClass(int i) : type(MyType::INT), data(new int(i), int_deleter)
      {
         std::cout << "Constructor with int\n";
      }

      MyClass(float i) : type(MyType::FLO), data(new float(i), float_deleter)
      {
         std::cout << "Constructor with float\n";
      }

      MyClass() : type(MyType::NO_TYPE)
      {
         std::cout << "Constructor (default)\n";
      }

      // Can be a private member function.
      static std::shared_ptr<void> make_data(const MyClass &from)
      {
         switch ( from.type )
         {
            case MyType::INT:
               return std::shared_ptr<void>(new int (*((int*)from.data.get())), int_deleter);

            case MyType::FLO:
               return std::shared_ptr<void>(new float (*((float*)from.data.get())), float_deleter);

            default:
               return {};
         }

         return {};
      }

      MyClass(MyClass &from) : type(from.type), data(make_data(from))
      {
         std::cout << "Copy constructor\n";
      }

      MyClass(const MyClass &from) : type(from.type), data(make_data(from))
      {
         std::cout << "Copy constructor\n";
      }

      ~MyClass()
      {
         std::cout << "Destructor for type "  << this->type << "\n";
      }
};

void test_int()
{
   MyClass c1(10);
   MyClass c2(c1);
}

void test_float()
{
   MyClass c1(10.2f);
   MyClass c2(c1);
}

int main()
{
   test_int();
   test_float();
}

输出:

Constructor with int
Copy constructor
Destructor for type 1
Deleting an int*
Destructor for type 1
Deleting an int*
Constructor with float
Copy constructor
Destructor for type 2
Deleting a float*
Destructor for type 2
Deleting a float*

我无法说明使用 shared_ptr<void> 的价值,但它可以作为指针的通用容器。

这些是有效的:

int i = 42;
std::shared_ptr<void> spVoid = std::make_shared<int>(i);

float f = 3.14f;
std::shared_ptr<void> spVoid = std::make_shared<float>(f);

当最后一个引用消失时,将调用最初实例化项的正确析构函数。通过 class 实例亲自查看。

struct Foo
{
    Foo()
    {
        std::cout << "Foo constructor" << std::endl;
    }

    ~Foo()
    {
        std::cout << "Foo destructor" << std::endl;
    }
};

int main()
{
    std::shared_ptr<void> spVoid = std::make_shared<Foo>();
    return 0;
}

我对向上转换 shared_ptr 给出的唯一警告是关于跨越 DLL 边界的。如果 shared_ptr 实例在 DLL 或共享库单元之间传递,原始类型(和正确的析构函数)可能会丢失。

您可以提供删除器:(默认的是正确的,因此可以使用 std::make_shared)。

class MyClass {
public:
    MyType type = MyType::NO_TYPE;
    std::shared_ptr<void> data;

    MyClass() = default;

    MyClass(int i) { set(i); }
    MyClass(float f) { set(f); }

    void Copy(const MyClass &from)
    {
        switch (from.type) {
            case MyType::INT: set(*((int*)from.data.get());
            case MyType::FLO: set(*((float*)from.data.get());
            case MyType::NO_TYPE: set();
        }
    }

    MyClass(const MyClass &from) {
        std::cout << "Copy constructor\n";
        Copy(from);
    }

    void set()
    {
        type = MyType::NO_TYPE;
        data = nullptr;
    }

    void set(int i)
    {
        type = MyType::INT;
        data = std::make_shared<int>(i);
    }

    void set(float f)
    {
        type = MyType::FLO;
        data = std::make_shared<float>(f);
    }

    ~MyClass() = default;
};