4

C ++入門書15.8で、作者が「ハンドルクラスと継承」について話したとき、彼は次のように述べました。

C ++の一般的な手法は、いわゆるカバークラスまたはハンドルクラスを定義することです。ハンドルクラスは、基本クラスへのポインタを格納および管理します。そのポインタが指すオブジェクトのタイプは異なります。ベースタイプまたは派生タイプのオブジェクトを指すことができます。ユーザーは、ハンドルを介して継承階層の操作にアクセスします。ハンドルはそのポインターを使用してこれらの操作を実行するため、仮想メンバーの動作は、ハンドルが実際にバインドされているオブジェクトの種類に応じて実行時に変化します。したがって、ハンドルのユーザーは動的な動作を取得しますが、ポインターの管理について心配する必要はありません。

上記は私へのスマートポインタのように聞こえます。ただし、この「ハンドル」の使用法は少し異なります。

Handle h(Derived()); // noticed a derived object, not a pointer is used to initialize the handle!
h->func();

そして、ハンドルの後者の実装(以下に表示)は、作成者がハンドルについてコメントしていることを証明します。

ハンドルのユーザーには、ハンドルをアタッチできる独自のオブジェクトを作成してもらいたいと思います。ハンドルは、適切なタイプの新しいオブジェクトを割り当て、ユーザーのオブジェクトをその新しく割り当てられたオブジェクトにコピーします。そうすれば、ハンドルクラスがオブジェクトを所有し、オブジェクトに接続されている最後のハンドルがなくなるまでオブジェクトが削除されないことを保証できます。

コード:

public:
    Base(){}
    virtual int func(std::size_t n) const {}
    virtual Base* clone() const { return new Base(*this); }
};

class Derived : public Base {
public:
    Derived():Base(){}
    int func(std::size_t) const; // redefine
    Derived* clone() const { return new Derived(*this); }
};

class Handle {
public:
    Handle(const Base &b):p(b.clone()), use(new std::size_t(1)) { }
private:
    Base *p;
    std::size_t *use; // pointer to shared use count
};

私の質問は、これは本当にC ++またはOOでのハンドルの典型的な意味ですか?ハンドルは、内部でuse-countを保持する単なる基本型のスマートポインター、またはより一般的には、システムがそれが何を指し、それをどう処理するかを知っている魔法のint型である可能性があると思いました。ここでこのハンドルがユーザーオブジェクトのコピーを作成し、そのコピーに基づいてuse-countを実行する必要があるのはなぜですか?

4

1 に答える 1

5

現代の C++ の慣用的な同等物は次のようになります。

std::shared_ptr<Base> h{std::make_shared<Derived>()};

一般に、「ハンドル」は何らかの形の型消去を意味するため、ハンドルを扱うコードは機能の実装方法の詳細を知る必要はありません。

あなたが示したコードがcloneオブジェクトをコピーするために使用している理由は、正確に型消去を実行するためです。メソッドが呼び出された時点cloneで、オブジェクトの実際のタイプの知識Derivedは消去されます。実装の品質に関しては、コードは非スレッド セーフであり、これは従来の C++ には関係ありませんが、スレッド対応メモリ モデルを持つ最新の C++ には問題です。また、明らかに例外セーフではありません。などの標準ライブラリ クラスに例外の安全性を委譲する方がはるかに優れていますshared_ptr。最後に、このBaseクラスには仮想デストラクタがありません。

于 2012-09-07T13:54:53.160 に答える