我在我的应用程序中使用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?
但我不确定这一点。
答案 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