1. ホーム
  2. c++

[解決済み] クラスメンバにスマートポインタを使用する

2022-04-22 11:51:29

質問

C++11のクラス・メンバーとしてのスマート・ポインタの使い方を理解するのに苦労しています。 スマート・ポインタについて多くの本を読み、どのようにすれば unique_ptrshared_ptr / weak_ptr は一般的に働きます。わからないのは、実際の使用方法です。誰もが unique_ptr を、ほぼ常に使用するようにします。しかし、このようなものをどのように実装すればよいのでしょう。

class Device {
};

class Settings {
    Device *device;
public:
    Settings(Device *device) {
        this->device = device;
    }

    Device *getDevice() {
        return device;
    }
};    

int main() {
    Device *device = new Device();
    Settings settings(device);
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

例えば、ポインターをスマートポインターに置き換えるとします。A unique_ptr のため、うまくいきません。 getDevice() ということですよね?だから、そういう時にこそ shared_ptrweak_ptr ? を使用する方法はありません。 unique_ptr ? ほとんどの場合 shared_ptr の方が、よほど小さなスコープでポインタを使うのでない限り、理にかなっているのではないでしょうか?

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> device) {
        this->device = device;
    }

    std::weak_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::weak_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

ということでいいのでしょうか?ありがとうございました。

どのように解決するのですか?

<ブロッククオート

A unique_ptr のために動作しません。 getDevice() ということですね?

いいえ、必ずしもそうではありません。ここで重要なのは、適切な オーナーシップポリシー を使用します。 Device オブジェクトの所有者、つまり (スマート) ポインタが指すオブジェクトの所有者になるのは誰かということです。

のインスタンスになるのでしょうか? Settings オブジェクト 単独 ? を使用するのでしょうか? Device オブジェクトは自動的に破棄されなければなりません。 Settings それとも、そのオブジェクトよりも長持ちさせるべきでしょうか?

最初の場合 std::unique_ptr が必要です。 Settings は、指されたオブジェクトの唯一の(ユニークな)所有者であり、その破壊に責任を持つ唯一のオブジェクトです。

この前提で getDevice() は、単純な 観測中 ポインタ(観察ポインタとは、指されたオブジェクトを生かさないポインタのことです)。最も単純な観測ポインタは生ポインタである。

#include <memory>

class Device {
};

class Settings {
    std::unique_ptr<Device> device;
public:
    Settings(std::unique_ptr<Device> d) {
        device = std::move(d);
    }

    Device* getDevice() {
        return device.get();
    }
};

int main() {
    std::unique_ptr<Device> device(new Device());
    Settings settings(std::move(device));
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

[ 注1. なぜ私がここで生のポインタを使うのか、不思議に思われるかも知れません。実際、これは貴重な警告なのですが、正しい文脈に置くことが重要です。 手動でのメモリ管理に使用する場合 を使ったオブジェクトの割り当てや解放などです。 newdelete . 純粋に参照セマンティクスを実現し、非所有で観察中のポインタを受け渡す手段として使う場合、ぶら下がったポインタを参照解除しないように注意しなければならないという事実を除けば、生のポインタに本質的に危険なものはありません。 - 注1を終了します。 ]

[ 注2. コメントで出てきたように、所有権が一意であるこの特殊なケースで そして の場合、所有するオブジェクトは常に存在することが保証されています(つまり、内部データ・メンバである device は決して nullptr ), 関数 getDevice() はポインタではなく、参照を返すことができる(そしておそらくそうすべき)。これは正しいのですが、私はここで生のポインタを返すことにしました。 devicenullptr また、手動でのメモリ管理に使用しない限り、生ポインタは問題ないことを示すためです。 - ノート2を終了する ]


もちろん、もしあなたが Settings オブジェクトは ではなく は、デバイスの排他的所有権を持つ。このようなケースは例えば Settings オブジェクトの破壊を意味するものではありません。 Device オブジェクトも同様です。

これは、プログラムの設計者であるあなたにしかわからないことです。あなたの提供する例からは、そうであるかどうか私にはわかりません。

それを理解するために、「他のオブジェクトは? Settings を保持する権利を有するもの。 Device オブジェクトへのポインタを保持する限り、受動的なオブザーバーではなく、生き続けることができるのです。もし本当にそうであるなら、そのためには 共有オーナーシップポリシー というものである。 std::shared_ptr を提供しています。

#include <memory>

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> const& d) {
        device = d;
    }

    std::shared_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device = std::make_shared<Device>();
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

注目すべきは weak_ptr を観察する 言い換えれば、指されたオブジェクトに対する他のすべての所有ポインタがスコープ外に出た場合、指されたオブジェクトを生かし続けることはできません。

の利点は weak_ptr は、通常の生ポインタよりも、安全に weak_ptr ぶらさがり を指しているかどうか (すなわち、有効なオブジェクトを指しているかどうか、 元々指していたオブジェクトが破棄されているかどうか)。これを行うには expired() メンバ関数は weak_ptr オブジェクトを作成します。