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_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...
}
それが行く方法ですか?どうもありがとう!
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:コメントに現れたように、所有権が一意であるこの特定のケースではおよび所有オブジェクトは常に保証されます存在する(つまり、内部データメンバdevice
がnullptr
になることはありません)、関数getDevice()
は、ポインタではなく参照を返すことができます(おそらくそうすべきです)。これは本当ですが、device
がnullptr
である可能性がある場合に一般化できる短い答えであり、生のポインタが手動のメモリ管理に使用しない限り、問題ありません。-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_ptr
はobservingポインターであり、所有ポインターではないことに注意してください。つまり、すべての場合、ポイントされたオブジェクトが生き続けることはありません。先のとがったオブジェクトへの他の所有ポインターは範囲外になります。
weak_ptr
の通常の生のポインタに対する利点は、weak_ptr
がdanglingかどうか(つまり、有効なオブジェクトを指しているか、最初に指していたオブジェクトが破壊された場合)。これは、weak_ptr
オブジェクトのexpired()
メンバー関数を呼び出すことで実行できます。
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_ptr
sが削除されると、ポインターが削除されます。 weak_ptr
からポインターが必要な場合、lock
を使用して、ポインターを取得します(存在する場合)。