173

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...
}

ポインタをスマートポインタに置き換えたいとしましょう。Aunique_ptrが原因で機能しませんgetDevice()よね?だからそれは私が使う時ですshared_ptrそしてweak_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...
}

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

4

2 に答える 2

218

Aunique_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: 生のポインターは悪い、安全ではない、危険であると誰もが言い続けるのに、なぜここで生のポインターを使用しているのか不思議に思うかもしれません。実際には、これは貴重な警告ですが、正しいコンテキストに置くことが重要です。手動のメモリ管理、つまり、およびを介したオブジェクトの割り当てと割り当て解除に使用する場合、new rawポインタは不適切ですdelete。参照セマンティクスを実現し、所有していない監視ポインターを渡す手段として純粋に使用する場合、ダングリングポインターを逆参照しないように注意する必要があるという事実を除いて、生のポインターには本質的に危険なものはありません。-END NOTE1 ]

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


もちろん、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ます。

于 2013-03-26T23:06:56.337 に答える
1
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_ptrs、もう1つはすべてのポインター(shared_ptrおよびweak_ptr)です。すべてshared_ptrのが削除されると、ポインタが削除されます。からポインタが必要な場合は、ポインタが存在する場合はweak_ptrlockを使用してポインタを取得する必要があります。

于 2013-03-26T22:53:44.913 に答える