0


0

クラス、コンストラクター、およびポインタークラスメンバー

オブジェクトの参照について少し混乱しています。 以下の例を確認してください。

class ListHandler {
public:
   ListHandler(vector &list);
private:
   vector list;
}

ListHandler::ListHandler(vector &list) {
   this->list = list;
}

内部のため

vector list;

定義、ここで私は右のメモリを浪費するだろうか? したがって、正しいものは次のようになります。

class ListHandler {
public:
   ListHandler(vector* list);
private:
   vector* list;
}

ListHandler::ListHandler(vector* list) {
   this->list = list;
}

ListHandler::~ListHandler() {
   delete list;
}

基本的に私が望むのは、ベクトルを作成してListHandlerに渡すことだけです。 このベクトルは、ListHandler自体以外では使用されないため、ListHandlerが他のすべてのことやクリーンアップなどを行うことを期待しています。 もの。

5 Answer


1


最初の例は必ずしもメモリを浪費するわけではなく、「this→ list = list;」でベクター全体のコピーを作成するだけです。行(必要に応じて、コンテキストによって異なります)。 これは、vectorのoperator =メソッドがその時点で呼び出され、vectorがそれ自体とそのすべての内容の完全なコピーを作成するためです。

2番目の例は、ベクトルのコピーを作成するのではなく、メモリアドレスを割り当てるだけです。 ListHandlerコンストラクターの呼び出し元は、ListHandlerがポインターの制御を引き継いでいることに気づきます。これは、最終的にメモリーの割り当てを解除するためです。


1


それは、呼び出し元がリストを使い続けることを期待するかどうか(その場合、リストを削除しない方がいいでしょう。そして、予想以上に変更することを心配する必要があります)それへのポインタを保持しないでください)。

クラスのドキュメントが、呼び出し側がnewでリストを割り当て、コンストラクターを呼び出すときに所有権をクラスに引き継ぐことである場合、ポインターを保持することは問題ありません(ただし、「delete list」を書く必要がないようにauto_ptrを使用します)あなた自身と例外の安全性を心配してください)。


1


基になるベクトルを共有するかどうかによって異なります。 一般に、オブジェクトの所有権の問題を排除するため、可能な限り共有を避けることをお勧めします。 共有しない場合:

class ListHandler
{
    public:
        ListHandler(const std::vector& list) : _list(list) {}
        ~ListHandler(){}
    private:
        std::vector _list;
};

あなたの例とは異なり、オリジナルが変更されないため、 `const`にしていることに注意してください。 ただし、同じ基になるオブジェクトに固執して共有したい場合は、次のようなものを使用できます。

class ListHandler
{
    public:
        ListHandler(std::vector& list) : _list(&list) {}
        ~ListHandler(){}
    private:
        std::vector* _list;
};

この場合、呼び出し元をオブジェクトの所有者のままにしておくことに注意してください(したがって、リストがリストハンドラオブジェクトの存続期間中、リストが後で割り当て解除されるようにするのは呼び出し元の責任です)。 所有権を引き継ぐ例も可能です。

class ListHandler
{
    public:
        ListHandler(std::vector* list) : _list(list) {}
        ListHandler(const ListHandler& o) : _list(new std::vector(o._list)) {}
        ~ListHandler(){ delete _list; _list=0; }

        ListHandler& swap(ListHandler& o){ std::swap(_list,o._list); return *this; }
        ListHandler& operator=(const ListHandler& o){ ListHandler cpy(o); return swap(cpy); }
    private:
        std::vector* _list;
};
上記は確かに可能ですが、個人的には好きではありません…​ 単にスマートポインタークラスではないオブジェクトが別のオブジェクトへのポインターの所有権を取得するのはわかりにくいです。 私がそれをしていたら、次のようにstd

vectorをスマートポインターコンテナーにラップすることで、より明確にします。

class ListHandler
{
    public:
        ListHandler(const boost::shared_ptr< std::vector >& list) : _list(list) {}
        ~ListHandler(){}
    private:
        boost::shared_ptr< std::vector > _list;
};

私は、所有権を伝える際に上記がはるかに明確だと感じています。 ただし、リストに沿って渡すこれらのさまざまな方法はすべて受け入れられます…​ 誰が何を所有するのかをユーザーに知らせるだけです。


1


それはすべて、あなたが望むものと、あなたが保証できるポリシーに依存します。 最初の例で「間違った」ものは何もありません(ただし、異なる名前を選択することで明示的に `this→`を使用することは避けます)。 ベクターのコピーを作成しますが、それが正しいことかもしれません。 それが最も安全なことかもしれません。

しかし、同じベクトルを再利用したいようです。 リストがListHandlerを超えて存続することが保証されている場合は、ポインターの代わりに参照を使用できます。 秘trickは、次のように、参照メンバー変数をコンストラクターの_初期化リスト_で初期化する必要があることです。

class ListHandler
{
public:
    ListHandler(const vector &list)
    : list_m(list)
    {
    }
private:
    vector& list_m;
};

初期化リストは、コロンの後、ボディの前のビットです。

ただし、これは、ポインタを使用してデストラクタで `delete`を呼び出す2番目の例とは異なります。 これは、ListHandlerがリストの所有権を引き継ぐ3番目の方法です。 しかし、コードには危険が伴います。「delete」を呼び出すことで、リストに「new」が割り当てられたと見なされるためです。 このポリシーを明確にする1つの方法は、所有権の変更を識別する命名規則(「adopt」プレフィックスなど)を使用することです。

ListHandler::ListHandler(vector *adoptList)
: list_m(adoptList)
{
}

(これは、名前の変更と初期化リストの使用を除いて、あなたのものと同じです。)

そのため、次の3つの選択肢があります。

  1. リストをコピーします。

  2. 他の誰かが所有しているリストへの参照を保持します。

  3. 誰かが「new」で作成したリストの所有権を引き受けます。

参照カウントを行うスマートポインターなど、さらに多くの選択肢があります。


0


単一の「正しい方法」はありません。ただし、2番目の例は非常に貧弱なスタイルになります。これは、ListHandlerが構築されるときにベクターの所有権を取得するためです。 すべての「新しい」は、可能な限り「削除」と密接にペアにする必要があります。真剣に、それは非常に高い優先度です。

vector`が ListHandler`だけ存続している場合、ListHandler内で存続する可能性があります。 ヒープ上に置いても、スペースをあまり消費しません。 実際、ヒープはオーバーヘッドを追加します。 したがって、これは「新しい」仕事ではありません。

また考慮するかもしれません

ListHandler::ListHandler(vector &list) {
   this->list.swap( list );
}

初期化リストをクリアし、ベクターのコンテンツをコピーする時間とメモリのオーバーヘッドを避けたい場合。