web-dev-qa-db-ja.com

C ++が永続オブジェクトを返す

私は現在、C#のバックグラウンドから来た後、C++のベストプラクティスを学ぼうとしています。オブジェクトを処理するには3つの方法があることを理解しています。

  • 値による(オブジェクトは、関数に渡されたり、関数から渡されたりするとコピーまたは移動されます)
  • 参照により
  • 共有ポインターまたは一意のポインター(生のポインターは、本当に必要な場合を除いて、眉をひそめています)

一般に、共有ポインターの使用を避けることは良い習慣だと思いますが、最近多くのコードを開発しているので、最初に何かを値型として定義し、それを共有ポインターにする必要があることに気づきました。この状況は頻繁に発生するため、私のシステムのほとんどすべてのオブジェクトが共有ポインターにあります!これは間違っているようです。

ほとんどのクラスは次のようになります。

class Container
{
public:
    // ...other functions

    std::shared_ptr<Thing> GetThing() const;
    std::vector<std::shared_ptr<Thing>> GetThings() const;

private:
    std::shared_ptr<Thing> thing;
    std::vector<std::shared_ptr<Thing>> things;
}

最初はこのクラスにはタイプThingの値オブジェクトが含まれていましたが、他のクラスはこれらのオブジェクトにアクセスする必要があるため、「ゲッター」関数から返されたときにコピーされないように、共有ポインターに入れました。これは、これらのオブジェクトに変更が発生した場合、それらの状態はコンテナと現在「もの」にアクセスしているオブジェクトと一致することを意味します。

なぜこれは間違っていると感じますか、そしてこのアプローチをどのように改善できますか?これを行う正しい「C++」の方法は何ですか?

7
innova

これらのオブジェクトをどのように処理するかは明確ではありません。

コピー不可能なクラスをコピーしたい場合は、shared_ptrを使用しても問題ありません。

オブジェクトをコピーする場合は、値を返します。

これらのオブジェクトへのアクセスを提供するだけの場合は、参照を使用します。

class Container
{
public:
    // ...other functions

    const Thing& GetThing() const;
    const std::vector<Thing>& GetThings() const;

private:
    Thing thing;
    std::vector<Thing> things;
};
6
BЈовић

shared_ptrに戻る前にconst refを返す/渡します。オブジェクトを変更することを許可せずに、参照渡しを許可します。このため、全体を通してconst-correctnessを維持するように注意する必要があります。

class Container
{
public:
    // ...other functions

    const Thing& GetThing() const;
    Thing& GetThing();

    const std::vector<Thing>& GetThings() const;
    std::vector<Thing>& GetThings();

private:
    Thing thing;
    std::vector<Thing> things;
}

重要なことは、所有権についての推論です。値による保管と参照による受け渡しを使用すると、オブジェクトの所有者を正確に把握し、オブジェクトを破棄する責任を負う人を把握できます。

2
ratchet freak

Container内の内部値を変更したいので、これを行っているようです。だからあなたは次のようなことをしています:

void foo(Container &container) {
   container.GetThing().SetFoo(12);
}

問題は、このようにContainerの内部状態を変更することになっていないということです。 Containerのメソッドのみが変更する必要があります。したがって、この関数はおそらくコンテナ内のメソッドでなければなりません。

void Container::foo() {
    thing.SetFoo(12);
}

Container以外のものを本当に変更する必要がある場合は、より明示的なものを選択する必要があります。

void foo(Container &container) {
    Thing thing = container.GetThing();
    thing.SetFoo(12);
    container.SetThing(thing);
}

しかし、Containerの内部状態のGet/Setメソッドを持つことは、コードの匂いです。本当にContainerの一部であるはずの他のコードがあることを示唆しています。

1
Winston Ewert