web-dev-qa-db-ja.com

クラスメンバーにスマートポインターを使用する

C++ 11のクラスメンバとしてのスマートポインタの使用法を理解するのに問題があります。スマートポインターについて多くのことを読みましたが、unique_ptrおよびshared_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...
}

ポインターをスマートポインターに置き換えたいとしましょう。 getDevice()が原因でunique_ptrは機能しませんか? 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...
}

それが行く方法ですか?どうもありがとう!

143
michaelk

getDevice()が原因でunique_ptrは機能しませんか?

いいえ、必ずしもそうではありません。ここで重要なのは、あなたのDeviceオブジェクトに適切な所有権ポリシーを決定することです。つまり、誰が(スマート)ポインタによってポイントされたオブジェクトの所有者になります。

Settingsオブジェクトのインスタンスaloneになりますか? Deviceオブジェクトが破棄された場合、Settingsオブジェクトは自動的に破棄される必要がありますか、それともそのオブジェクトより長生きする必要がありますか?

最初のケースでは、std::unique_ptrが必要です。これは、Settingsを、尖ったオブジェクトの唯一の(一意の)所有者にし、その破壊を担当する唯一のオブジェクトにするためです。

この仮定の下で、getDevice()は、単純なobservingポインターを返す必要があります(監視ポインターは、ポイントされたオブジェクトを存続させないポインターです)。最も単純な種類の監視ポインターは、生のポインターです。

#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:生のポインターが悪い、安全でない、危険であるとみんなが言い続けているのに、なぜここで生のポインターを使用しているのか疑問に思うかもしれません。実際には、これは貴重な警告ですが、正しいコンテキストに置くことが重要です。生のポインタが悪い手動メモリ管理を実行するために使用する場合、つまりnewを介してオブジェクトの割り当てと割り当て解除およびdelete。参照セマンティクスを達成し、非所有の観察ポインターを渡す手段として純粋に使用される場合、ぶら下がりポインターを逆参照しないように注意する必要があるという事実を除いて、生ポインターには本質的に危険なものは何もありません-END NOTE 1]

[注2:コメントに現れたように、所有権が一意であるこの特定のケースではおよび所有オブジェクトは常に保証されます存在する(つまり、内部データメンバdevicenullptrになることはありません)、関数getDevice()は、ポインタではなく参照を返すことができます(おそらくそうすべきです)。これは本当ですが、devicenullptrである可能性がある場合に一般化できる短い答えであり、生のポインタが手動のメモリ管理に使用しない限り、問題ありません。-END NOTE 2]


もちろん、Settingsオブジェクトがnotにデバイスの排他的所有権を持っている場合、状況は根本的に異なります。たとえば、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_ptrobservingポインターであり、所有ポインターではないことに注意してください。つまり、すべての場合、ポイントされたオブジェクトが生き続けることはありません。先のとがったオブジェクトへの他の所有ポインターは範囲外になります。

weak_ptrの通常の生のポインタに対する利点は、weak_ptrdanglingかどうか(つまり、有効なオブジェクトを指しているか、最初に指していたオブジェクトが破壊された場合)。これは、weak_ptrオブジェクトのexpired()メンバー関数を呼び出すことで実行できます。

188
Andy Prowl
class Device {
};

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

    }

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

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

week_ptr は、参照ループにのみ使用されます。ディペンデンシーグラフは、非循環有向グラフでなければなりません。共有ポインターには、2つの参照カウントがあります。1つは shared_ptr s、1つはすべてのポインター(shared_ptrおよびweak_ptr)です。すべてのshared_ptrsが削除されると、ポインターが削除されます。 weak_ptrからポインターが必要な場合、lockを使用して、ポインターを取得します(存在する場合)。

0
Naszta