我应该使用哪个智能指针?

时间:2012-08-06 12:24:51

标签: c++

我正在考虑一个由许多不同类型(属性,父级,子级等)组成的模型。每种类型都与c api中的一组函数相关联。例如:

Type "Properties":
  char* getName(PropertiesHandle);
  char* getDescription(PropertiesHandle);

Type "Parent"
  PropertiesHandle getProperties(ParentHandle);
  ChildHanlde getFirstChild(ParentHandle);

Type "Child"
  PropertiesHandle getProperties(ChildHandle);
  ParentHanlde getParent(ChildHandle);
  ChildHandle getNextChild(ChildHandle);

我为这个c api库创建了一组C ++接口,如下所示:

class IProperties
{
public:
  virtual std::string getName() = 0;
  virtual std::string getDescription() = 0;
};

class IParent
{
public:
  virtual std::shared_ptr<IProperties> getProperties() = 0;
  virtual std::shared_ptr<IChild> getFirstChild() = 0;
};

class IChild
{
public:
  virtual std::shared_ptr<IProperties> getProperties() = 0;
  virtual std::shared_ptr<IParent> getParent() = 0;
  virtual std::shared_ptr<IChild> getNextChild() = 0;
};

然后我通过类Properties,Parent和Child实现这些接口。

因此,Child实例将通过其构造函数获取其特定的ChildHandle,其getParent函数将如下所示:

std::shared_ptr<IParent> getParent()
{
    // get the parent handle and wrap it in a Parent object
    return std::shared_ptr<IParent>(new Parent(_c_api->getParent(_handle)));
}

在您看来,我在这里返回shared_ptr是否合理。我不能使用std :: unique_ptr,因为Google Mock需要参数并且返回模拟方法的值是可复制的。我在我的测试中通过Google Mock嘲笑这些界面。

我也在考虑将来如何优化事物,这可能会提供可能的循环引用。如果使用缓存来避免多次调用C api(例如,不需要子节点多次建立其父节点),并且使用Child构造函数获取其父节点,则可能会导致这种情况。这将需要使用weak_ptrs,这会改变接口和我的很多代码...

3 个答案:

答案 0 :(得分:2)

返回shared_ptr并没有错,但我会试着说服你,这可能不是最好的选择。

通过使用智能指针,您可以获得安全的优势,但API的用户会失去使用最符合其需求的智能指针类型的灵活性,而必须始终使用shared_ptr

这还取决于您对灵活性的重视程度,但我个人会考虑返回一个裸指针并允许用户使用他想要的智能指针。当然,如果由于某种原因我有必要使用shared_ptr,我会。

答案 1 :(得分:2)

关键问题是:返回指针的语义是什么?

  • 如果返回的父/子/属性对象具有返回(可能在某种意义上,拥有)对象的生命周期独立,则返回shared_ptr是合理的:这表明调用者和被调用者具有相等的权限来决定对象的生命周期

    std::shared_ptr<IChild> child = parent->getFirstChild();
    // now I can keep child around ... if parent is destroyed, one
    // orphaned subtree is magically kept around. Is this desirable?
    
  • 如果返回的对象在被调用者自己的生命周期中具有生存期依赖,则:

    • shared_ptr 错误地建议调用者将返回对象的生命周期延长到被调用者的生命周期之外是有意义的
    • unique_ptr 错误建议转让所有权
    • 原始指针显式做出任何误导性承诺,但不提供任何关于正确使用的提示

因此,如果调用者只是获得对象内部状态的工作引用,而没有所有权转移或对象生存期的延长,则不会建议使用任何智能指针。

考虑只返回一个参考。

答案 2 :(得分:0)

shared_ptr很好,但它确实为最终用户提供了一些限制,例如C ++ 11支持。原始指针或允许它们定制智能指针的特征可以为最终用户提供更大的灵活性。

无论指针如何,我建议小心实现引入的语义。使用当前实现,为每个访问者调用实例化一个新的包装,等价检查将失败。请考虑以下代码:

auto child = parent->getFirstChild();
if ( parent == child->getParent() ) // Will be false, as they point to different
                                    // instantiations of Parent.
  ...

if ( child->getParent() == child->getParent() ) // False for the same reason.
  ...

auto sibling = child->getNextChild();
if ( parent == sibling->getParent() ) // Also false for the same reason.
  ... 

此外,使用std::shared_ptr时,值得考虑使用std::make_shared来减少分配时产生的一些开销。