使用 unique_ptr 来包装和解开 void*

问题描述

我的目标是创建 unique_ptr ,其中 void* 可以指向任何对象类型。这给了我一种使用 unique_ptr 指向共享相同签名的状态对象的方法。但是下面的代码似乎不符合我的期望,其中 p.get() 应该等于 v 和 s1 地址。我怀疑它必须尝试将 unqiue_ptr 包装在 s1 之上以拥有它。但是地址变了。

struct State1 
{
public:
  std::string name;
  std::string address;
};

int main() {

  auto s1 = new State1;
  s1->name = "name";
  std::cout<<"s1 address: "<<s1<<"\n";
  auto v = (void*)s1;
  std::cout<<"v address: "<<v<<"\n";
  auto p = std::make_unique<void*>(s1);
  std::cout<<"p.get address: "<<p.get()<<"\n";
  auto r =  static_cast<State1*>((void*)p.get());
  std::cout<<"r address: "<<r<<"\n";
}

s1 address: 0x55dcf389aeb0
v address: 0x55dcf389aeb0
p.get address: 0x55dcf389b310
r address: 0x55dcf389b310

解决方法

std::unique_ptr<void*> 是一个唯一的指向void的指针的指针。指向对象的指针与被指向的对象是分开的。换句话说,指针和指向的对象具有不同的地址。 void指针的值是指向对象的地址,void指针的值是指向该对象的void指针的地址。

p.get() 为您提供唯一指针拥有的 void* 对象的地址(即指向的指针)。为了从(唯一)指向void的指针得到指向void的指针,必须通过指向void的(唯一)指针间接。这给出了等于 s1v 的地址:

std::cout << *p;

附言您永远不会删除动态分配的 State1,因此您会出现内存泄漏。

,

std::unique_ptr<void*> 是一个指向指针的智能指针。

您希望 unique_ptr<void> 作为第一步。

接下来你需要的是到达那个独特的 ptr 如何删除资源;默认情况下,唯一的 ptr 只调用 delete,这不适用于 void 指针。无法知道void指针中存储的类型,因此无法调用析构函数。

template<class T>
std::unique_ptr<void,void(*)(void*)>
wrap_ptr(T* ptr){
  return {ptr,[](void*ptr){ delete static_cast<T*>(ptr); }};
}

这里我们记住了如何通过存储一个指向知道真实类型的代码的函数指针来删除对象。

auto p = wrap_ptr(s1);

我怀疑代码可以满足您的需求。

顺便说一句,((void*)p.get()) C 所投射的隐藏了一条错误消息,说明您做错了什么。避免使用 C 风格的强制转换。

您将 void** 重新解释为 void* 的事实应该是一个危险信号。