C ++等效的C类实现的性能版本

时间:2013-03-10 11:49:16

标签: c++ smart-pointers pimpl-idiom

我正在学习C ++,而我遇到了这种情况,我希望在C ++中用C语言实现以下符号代码的等效高效版本。

<header.h>
struct Obj;
Obj* create(...);
void do_some_thing(Obj*);
void do_another_thing(Obj*);
void destroy(Obj*);

要求是:

  • 实现是在库(静态/动态)和 header不会公开除接口之外的任何细节
  • 它应该同样有效

使用虚函数显示接口(类似COM)不符合条件;这是一个启用多态的解决方案(通过相同的接口暴露多个实现),但事实并非如此,而且由于我不需要它带来的值,我不明白为什么我应该支付调用函数的成本通过2个间接指针。 所以我的下一个想法是pimpl成语:

<header.h>
class Obj {
public:
  Obj();
  ~Obj();
  void do_some_thing();
  void do_another_thing();
private:
  class Impl;
  smart_ptr<Impl>  impl_; // ! What should I use here, unique_ptr<> or shared_ptr<> ?
};

的shared_ptr&LT;&GT;似乎没有资格,我会支付原始实现中不存在的不必要的互锁增量/减量。 另一方面,unique_ptr&lt;&gt;使Obj不可复制。这意味着客户端无法通过值调用自己的Obj函数,而Obj只是指针的包装器,所以基本上他不能按值传递指针!他可以在原始版本中做到这一点。 (通过引用传递仍然不符合条件:他仍然传递指针指针)

那么在C ++中实现它的同样有效的方法是什么?

编辑: 我给了它更多的想法,我来到这个解决方案:

<header.h>
class ObjRef // I exist only to provide an interface to implementation
{            //  (without virtual functions and their double-level indirect pointers)
public:
  ObjRef();
  ObjRef(ObjRef);           // I simply copy pointers value
  ObjRef operator=(ObjRef); // ...
  void do_some_thing();
  void do_another_thing();
protected:
  class Impl;
  Impl*  impl_; // raw pointer here, I'm not responsible for lifetime management
};

class Obj : public ObjRef
{
  Obj(Obj const&);            // I'm not copyable
  Obj& operator=(Obj const&); // or assignable 
public:
  Obj(Obj&&);                 // but I'm movable (I'll have to implement unique_ptr<> functionality)
  Obj& operator=(Obj&&);
  Obj();
  ~Obj(); // I destroy impl_
  // and expose the interface of ObjRef through inheritance
};

现在我返回客户端Obj,如果客户端需要通过调用其他函数来分配Obj的用法,他可以将它们声明为

void use_obj(ObjRef o);

// and call them:
Obj o = call_my_lib_factory();
use_obj(o);

2 个答案:

答案 0 :(得分:0)

为什么不保留C原件?您不必在C版本中支付引用计数溢价的原因是C版本依赖于调用者来保留正在使用的Obj*的副本数量。

通过尝试确保替换是可复制的并且确保仅在最后一个引用被销毁时调用基础destroy方法,您对原始引用施加了额外的要求,因此它自然是正确的解决方案(在我看来shared_ptr)与原始解决方案相比有一些额外的费用。

答案 1 :(得分:-1)

我认为首先需要指向对象的指针是有原因的。因为最简单,最有效的方法是在堆栈上创建它:

{
  Obj x(...);
  x.do_some_thing();
  x.do_another_thing();
} // let the destructor handle `destroy()` when the object goes out of scope

但是说你需要在堆上创建它,无论出于何种原因,大部分都是如此简单,同样有效:

{
  std::unique_ptr<Obj> x = Obj::create(...); // if you want a separate `create` function
  std::unique_ptr<Obj> x = new Obj(...); // Otherwise, a plain constructor will do

  x->do_some_thing();
  x->do_another_thing();
} // as before, let the destructor handle destruction

不需要继承或接口或虚函数。你在编写C ++,而不是Java。 C ++的基本规则之一是“不要为你不使用的东西买单”。 C ++默认情况下与C 一样高效。您无需执行任何特殊操作即可获得良好的性能。如果您不需要,您只需使用具有性能成本的功能。

您的C版本中不需要引用计数,那么为什么要在C ++版本中使用引用计数(使用shared_ptr)?您不需要虚拟函数在C版本中提供的功能(它将通过函数指针实现),那么为什么要在C ++函数中创建任何虚拟函数?

但是C ++可以让你特别整理创建/破坏的东西,而这只会让你失去成本。所以使用它。在其构造函数中创建对象,并让其析构函数销毁它。只需将对象放在适当的范围内,这样当你希望它被销毁时它就会超出范围。它为你提供了更好的语法来调用“成员”函数,所以也要使用它。

  

另一方面,unique_ptr&lt;&gt;使Obj不可复制。这意味着客户端无法通过值调用自己的Obj函数,而Obj只是指针的包装器,所以基本上他不能按值传递指针!他可以在原始版本中做到这一点。 (通过引用传递仍然不符合条件:他仍然传递指针指针)

如果使用移动语义,客户端可以按值获取Obj。但是如果你想要复制对象,那就让它复制一下。使用我的第一个示例,并在堆栈上创建对象,并在需要按值传递时按值传递它。它包含任何复杂的资源(例如指向已分配内存的指针),然后确保编写适当的复制构造函数和赋值运算符,然后您可以创建对象(在堆栈上,或者您需要的任何地方),传递它按照您的意愿,通过引用,通过将其包装在智能指针中,或通过移动它(如果您实现必要的移动构造函数和移动赋值运算符)。