2

この状況に遭遇している間、私はC ++を学んでいます。そこでは、Cで次のシンボリックコードの同等に効率的なバージョンをC++で実装したいと考えています。

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

要件は次のとおりです。

  • 実装はライブラリ(静的/動的)で提供され、ヘッダーはインターフェイス以外の詳細を公開しません
  • 同様に効率的である必要があります

仮想関数を使用してインターフェース(COMのようなもの)を公開することはできません。これは、ポリモーフィズム(同じインターフェイスを介して公開される複数の実装)を有効にするソリューションですが、そうではありません。それがもたらす価値は必要ないため、関数の呼び出しにかかるコストを支払う必要がある理由がわかりません。 2つの間接ポインタを介して。だから私の次の考えは、単純なイディオムでした:

<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 <>は適格ではないようです。元の実装には存在しなかった、不要なインターロックされたインクリメント/デクリメントに対して支払います。一方、unique_ptr <>を使用すると、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);
4

2 に答える 2

0

Cのオリジナルを保持しないのはなぜですか?C バージョンで参照カウント プレミアムを支払う必要がなかった理由は、C バージョンではObj*、使用中の のコピー数の集計を呼び出し元に依存していたためです。

置換がコピー可能であることと、最後の参照が破棄された後にのみ基になるメソッドが呼び出されることを保証しようとすることで、destroy元のメソッドに追加の要件を課していることになります。 be shared_ptr) には、オリジナルよりも限定的な追加費用がかかります。

于 2013-03-10T12:55:12.637 に答える
-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

継承、インターフェイス、または仮想関数は必要ありません。あなたは Java ではなく C++ を書いています。C++ の基本的なルールの 1 つは、「使用しないものに料金を支払うな」です。C++ は、デフォルトで C と同等のパフォーマンスを発揮します。優れたパフォーマンスを達成するために特別なことをする必要はありません。あなたがしなければならないのは、必要のない場合、パフォーマンスにコストがかかる機能を使用しないことだけです。

C バージョンでは参照カウントは必要なかったのに、なぜ C++ バージョンで参照カウントを使用するのでしょうか ( を使用shared_ptr)? 仮想関数が C バージョン (関数ポインターを介して実装される) で提供する機能は必要なかったのに、なぜ C++ 関数で何かを仮想化するのでしょうか?

しかし、C++ を使用すると、特に作成/破棄を整理することができ、費用はかかりません。だからそれを使ってください。コンストラクタでオブジェクトを作成し、デストラクタで破棄します。そして、オブジェクトを適切なスコープに配置するだけで、破棄したいときにスコープ外になります。また、「メンバー」関数を呼び出すためのより優れた構文が提供されるため、それも利用してください。

一方、unique_ptr<> は Obj をコピー不可にします。つまり、クライアントは Obj を値で受け取る独自の関数を呼び出すことができず、Obj は単なるポインターのラッパーであるため、基本的に値でポインターを渡すことはできません! 彼は元のバージョンでそれを行うことができました。(参照渡しはまだ修飾されていません: 彼はまだポインタをポインタに渡しています)

移動セマンティクスを使用する場合、クライアントは Obj を値で受け取ることができます。ただし、オブジェクトをコピーしたい場合は、コピーさせてください。最初の例を使用して、スタック上にオブジェクトを作成し、値渡しが必要なときに値渡しするだけです。複雑なリソース (割り当てられたメモリへのポインターなど) が含まれている場合は、適切なコピー コンストラクターと代入演算子を必ず記述してから、オブジェクトを (スタック上または必要な場所に) 作成し、渡すことができます。必要に応じて、値によって、参照によって、スマート ポインターでラップするか、移動します (必要な移動コンストラクターと移動代入演算子を実装する場合)。

于 2013-03-10T15:22:07.993 に答える