私は現在、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++」の方法は何ですか?
これらのオブジェクトをどのように処理するかは明確ではありません。
コピー不可能なクラスをコピーしたい場合は、shared_ptrを使用しても問題ありません。
オブジェクトをコピーする場合は、値を返します。
これらのオブジェクトへのアクセスを提供するだけの場合は、参照を使用します。
class Container
{
public:
// ...other functions
const Thing& GetThing() const;
const std::vector<Thing>& GetThings() const;
private:
Thing thing;
std::vector<Thing> things;
};
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;
}
重要なことは、所有権についての推論です。値による保管と参照による受け渡しを使用すると、オブジェクトの所有者を正確に把握し、オブジェクトを破棄する責任を負う人を把握できます。
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
の一部であるはずの他のコードがあることを示唆しています。