[英]What is the correct way to make a polymorphic container using smart pointers?
我想做一個維護多態容器的 C++ class 。 例如,獸醫可以持有一份目前正在接受治療的寵物的清單。 如果我們使用原始指針,我們可以如下定義一個 vet。
class Vet{
std::vector<Pet*> pets;
public:
void addPet(Pet* pet);
};
我們可以如下添加寵物。
Vet vet;
vet.addPet(new Dog{});
vet.addPet(new Cat{});
在這種情況下,應該讓 Vet class 的析構函數負責刪除 pets 中維護的動態分配的pets
。 為了避免這種情況,我想使用智能指針。 但是,在我能夠正確和干凈地實現此類代碼之前,需要澄清一些問題。 在以下問題上需要幫助。 提前致謝。
std::unique_ptr
還是std::shared_ptr
? 或者在什么情況下我應該使用std::unique_ptr
或std::shared_ptr
? 還是我應該 go 回到使用原始指針?std::unique_ptr
addPet
方法及其實現的方法簽名是什么?void addPet(std::unique_ptr<Pet> pet){
pets.push_back(std::move(pet));
}
void addPet(std::unique_ptr<Pet>& pet){
pets.push_back(std::move(pet));
}
僅當我按以下方式構建寵物時,此選擇才有效。
std::unique_ptr<Pet> dog = std::make_unique<Dog>();
vet.addPet(dog);
void addPet(PetType pet){
if(pet==PetType::Dog) pets.push_back(std::make_unique<Dog>());
//
}
std::shared_ptr
```addPet''' 方法的方法簽名及其實現是什么?void addPet(std::shared_ptr<Pet> pet){
pets.push_back(std::move(pet));
}
void addPet(const std::shared_ptr<Pet>& pet){
pets.push_back(pet);
}
假設你真的想在外面創建寵物,最好的方法是unique_ptr
並按值傳遞它,如下所示:
void addPet(std::unique_ptr<Pet> pet);
unique_ptr
因為您告訴讀者只有一個所有者。
按價值計算,因為這需要您在調用addPet
時使用臨時或顯式移動 unique_ptr 。 在外部非常明確地表明您正在移動/轉移所有權。
例如,您可以這樣做
auto pet = std::make_unique<Dog>();
vet.addPet(std::move(pet)); // move is required, making it explicit that ownership is transferred
或者
vet.addPet(std::make_unique<Dog>());
如果您在addPet
中接受對unique_ptr
的引用,則調用者將不知道在調用addPet
后提供的參數是否仍然有效。
這是一篇關於這個主題的好文章。
使用unique_ptr
。 shared_ptr
用於共享所有權,但您希望vet
擁有它包含的寵物,即不共享所有權。
unique_ptr
不需要出現在界面上。 添加貓狗的公共界面可以是這樣的:
class vet {
public:
void add_dog(const std::string& name);
void add_cat(const std::string& name);
};
當您想要轉移所有權時,使用unique_ptr
作為 function 參數。 沒有必要先讓調用者擁有寵物,然后讓他們將所有權轉移給vet
....除非那是你想要的,然后使用std::unique_ptr<Pet>
作為參數(沒有參考,但按價值)。
如果沒有必要,不要實施已經可用的資源控制。 您不必讓用戶構建 object,這增加了您不必向用戶公開您在內部使用的數據結構的好處。
class Vet {
private:
// Make sure Pet::~Pet() is virtual!
std::vector<std::unique_ptr<Pet>> pets;
public:
template <typename Species, typename... Args>
void addPet(Args...&& args) {
pets.emplace_back(std::make_unique<Species>(std::forward<Args>(args)...));
}
};
// ...
vet.addPet<Cat>(3, "black"); // assuming Cat::Cat(unsigned age, std::string color)
但是請注意,從語義上講,獸醫並不擁有寵物,他們會照顧它一段時間,然后釋放寵物。 獸醫在完成治療后摧毀寵物將是一個糟糕的獸醫。
無論如何,如果您想獲得寵物,您可以輕松添加類似的成員 function:
Pet& getPet(std::size_t const i) {
return *pets[i];
}
template <typename Species>
Species& get(std::size_t const i) {
return static_cast<Species&>(getPet(i));
// maybe consider dynamic_cast, here
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.