使用std :: shared_ptr <void>指向任何内容</void>

时间:2014-03-26 07:40:08

标签: c++ c++11 stl shared-ptr smart-pointers

我在我的应用程序中使用std::shared_ptr<void>来制作一个智能指针,它可以指向许多不同类型的数据结构,如结构,向量,矩阵......基本上任何东西。我要做的是将一些名称映射到他们的数据结构。

我正在使用哈希表执行映射:

std::unordered_map<std::string, std::shared_ptr<void>>

我可以将std::shared_ptr<void>返回的find()投回std::shared_ptr<my_type>吗?如果是这样,怎么样?

更重要的是,这是一种好的做法吗?随着应用程序的扩展,这会增加复杂性吗?或者,还有其他一些完全不同的优雅方法吗?

修改

可能不能使用`Boost.Any',因为它使用RTTI。

也不能为所有这些数据结构使用基类,因为它们中的一些是像std::vector这样的STL容器。

关于下面答案中讨论的shared_ptr删除问题,我读到shared_ptr执行类型删除并存储类型信息以了解要调用的析构函数。

Shared void pointers. Why does this work?

Why do std::shared_ptr<void> work

Why is shared_ptr<void> not specialized?

但我不确定这一点。

4 个答案:

答案 0 :(得分:4)

这不是好习惯。如果您不在std::shared_ptr旁边存储其他类型信息(您可以使用static_pointer_cast进行投射),那么您就会有未定义的行为。也许Boost.Any可以选择吗?

如果您想坚持std::shared_ptr<void>,请记得提供自定义删除功能(请参阅make shared_ptr not use delete)。

答案 1 :(得分:3)

如果你存储void*,那么在每个使用点你需要知道你输入的确切类型,作为演员 - void* - 并且 - 只有你去的时候才有效/来自完全相同的类型(例如,不是派生到虚空到基础)。

鉴于每个使用点都需要知道存储指针的类型,为什么要往返无效?

您存储的每种类型都有一张地图。这对每个应用程序的一个映射(运行时的O(类型数)内存,类似的编译时间)有适度的开销。

使用C ++ template,代码重复很少。

答案 2 :(得分:2)

  

我可以将find()返回的std :: shared_ptr转换回std :: shared_ptr吗?如果是这样,怎么样?

是。使用pointer casts。也就是说,在这种情况下,你需要从void *转换为具体类型的事实很可能是设计不佳的症状。

你应该(几乎)永远不需要从void *转换为强类型指针类型(&#34;几乎&#34;意味着我认为你永远不应该这样做,但可能有一些我没有考虑过的情况)

  

更重要的是,这是一种好的做法吗?

没有。根据经验,当您对内存地址的值感兴趣时,您应该只转换为void *,并且对存储的数据不感兴趣。

  

随着应用程序的扩展,这会增加复杂性吗?

我能想到的第一个问题是,您的类型不再在一个地方定义。这意味着,如果您在整个地方都有指针强制转换,当您决定更改类名时(或者您不再使用std :: string作为路径而是使用boost :: path对象,等等),你将不得不遍历代码并更新你添加的所有演员阵容中的所有类型。

第二个问题是,随着应用程序的扩展,它会在代码库中携带这个转换问题并将其传播,并使整个代码库的情况变得更糟。由于这个原因,您的应用程序代码将略微(或不那么轻微)维护。如果您在整个代码中有其他设计妥协,超过一定的规模,您的应用程序将变得难以/无法维护。

这种方法只会让你无法编写松散耦合的代码。

  

或者,还有其他完全不同的,优雅的方法吗?

查看代码并列出您在指针上使用的操作列表。然后,将这些操作作为纯虚函数添加到基类中。根据存储的值类型实现此基类的具体模板特化。

代码:

class pointer_holder { // get a better name
public:
    virtual void teleport_pointer() = 0; // example operation
    virtual ~pointer_holder() = 0;
};

template<typename T>
class type_holder: public pointer_holder {
public:
    // TODO: add constructor and such
    virtual void teleport_pointer() {
        // implement teleport_pointer for T
    }
    virtual ~type_holder();
private:
    T value_;
};

客户代码:

std::unordered_map<std::string, std::shared_ptr<pointer_holder>> your_map;
your_map["int"] = new type_holder<int>{10};
your_map["string"] = new type_holder<std::string>{"10"};

your_map["int"]->teleport_pointer(); // means something different for each
                                     // pointer type

答案 3 :(得分:1)

你可以。

#include <memory>
#include <iostream>
#include <string>

using namespace std;

class wild_ptr {
    shared_ptr<void> v;
public:
    template <typename T>
    void set(T v) {
        this->v = make_shared<T>(v);
    }

    template <typename T>
    T & ref() {
        return (*(T*)v.get());
    }
};

int main(int argc, char* argv[])
{
    shared_ptr<void> a;
    a = make_shared<int>(3);
    cout << (*(int*)a.get()) << '\n';
    cout << *static_pointer_cast<int>(a) << '\n'; // same as above

    wild_ptr b;
    cout << sizeof(b) << '\n';
    b.set(3);
    cout << b.ref<int>() << '\n';
    b.ref<int>() = 4;
    cout << b.ref<int>() << '\n';

    b.set("foo");
    cout << b.ref<char *>() << '\n';

    b.set(string("bar"));
    cout << b.ref<string>() << '\n';
    return 0;
}

输出:

3
3
8
3
4
foo
bar