【问题标题】:Using shared_ptr as output parameter使用 shared_ptr 作为输出参数
【发布时间】:2012-02-16 15:00:57
【问题描述】:

我正在开发一个从 DLL 导出多个类的 C++ API。

公共类接口应遵循以下约定:

  • 所有函数都返回错误代码。
  • 输出参数用于附加返回值。
  • 指针传递用于输出参数。
  • 按常量引用传递用于输入参数(基本类型按值传递)。
  • 当客户端应该取得输出参数的所有权时使用shared_ptr,否则使用普通指针。

示例界面:

typedef std::shared_ptr<Object> ObjectPtr;

class APIClass
{
    ErrorCode SetSomething(int i);
    ErrorCode IsSomethingSet(bool* ask);
    ErrorCode DoSomething();
    ErrorCode GetSomething(ObjectPtr* outObj);
}

示例用法:

ErrorCode res;
ObjectPtr obj;
res = myApiClass->GetSomething(&obj);

GetSomething 实现:

ErrorCode APIClass::GetSomething(ObjectPtr* outObj)
{
   ObjectPtr temp(new Object(), CleanUpFunction<Object>);

   // Do something with object temp.
   ...

   *outObj= temp;

   return OK;
}

以这种方式使用 shared_ptr 是否可以节省,或者我应该注意哪些问题?

【问题讨论】:

  • 您是否考虑过使用exceptions 而不是返回错误代码?
  • 是的,但我不想使用异常,因为跨 DLL 边界使用它们的限制
  • 编辑了这个问题,因为它太冗长了。

标签: c++ api error-handling shared-ptr


【解决方案1】:

这很好,但我会问在这种情况下是否真的需要共享指针。主要是因为您无法以任何理智的方式从shared_ptr 释放指针......这可能会导致以后出现问题。而shared_ptr 真正意味着底层资源的未指定或共享所有权。

我通常记录函数并使用类似的东西:

// Caller must delete the outObj once done.
ErrorCode APIClass::GetSomething( Object* & outObj )
{
  // I use auto_ptr so I can release it later...
  // Mostly I hate auto_ptr, but for this its invaluable.

  auto_ptr<Object> obj( new Object );
  ...
  outObj = obj.release();
  return OK;
}

这样,他们想要将指针存储到什么位置取决于客户端,并且很明显,对象的所有权传递给调用者。

然后客户端代码可以使用适当的容器。

Object * obj_raw;
ErrorCode ec = apiClass.GetSomething( obj_raw )
if( ec!=OK ) { .. do something with ec .. }
shared_ptr<Object> obj( obj_raw );

auto_ptr<Object> obj( obj_raw );

scoped_ptr<Object> obj( obj_raw); 

等等

请注意,如果您将函数定义更改为:

// Caller must delete the return value.
// On error, NULL is returned and e filled in appropriately.
Object* APIClass::GetSomething( ErrorCode & e )
{
   auto_ptr<Object> obj( new Object );
   ..
   e = OK;
   return obj.release();
}

//Now using it looks like this:
ErrorCode ec;
shared_ptr<Object> obj( apiObject.GetSomething(ec) ); 
if(!obj)
{
   .. do something with ec ..
}

【讨论】:

  • 感谢您的回复。跨 DLL 边界(至少在 Windows 上)传递指针存在问题,例如在 DLL 中分配内存并在客户端将其删除。见这里:msdn.microsoft.com/en-us/library/ms235460.aspx。这就是为什么我在创建 shared_ptr 时使用在 DLL (CleanUpFunction) 中定义的自定义删除器。
  • 但一般来说,您认为解决方案应该没问题吗?在哪种情况下可能无法稍后释放指针或为什么要释放它?
  • 我也在考虑与您提到的类似的替代设计,将错误代码作为可选参数传递,如下所示:ObjectPtr APIClass::GetSomething(ErrorCode* e = nullptr)。但还不能决定哪种设计更好。
  • @Hoschie0815 您可能希望稍后发布它的原因是将其传递给期望获得对象所有权的接口。
  • @Hoschie0815 shared_ptr 不会让您摆脱内存分配问题,因为 shared_ptr 构造函数分配了一个块(包含引用计数等),而它的析构函数释放了该块。如果它在 DLL 中创建并在 DLL 外部销毁,那么您又会遇到完全相同的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-17
  • 2013-11-30
  • 1970-01-01
  • 2020-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多