簡體   English   中英

使用智能指針制作多態容器的正確方法是什么?

[英]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 為了避免這種情況,我想使用智能指針。 但是,在我能夠正確和干凈地實現此類代碼之前,需要澄清一些問題。 在以下問題上需要幫助。 提前致謝。

  1. 我應該使用std::unique_ptr還是std::shared_ptr 或者在什么情況下我應該使用std::unique_ptrstd::shared_ptr 還是我應該 go 回到使用原始指針?
  2. 如果我選擇使用std::unique_ptr addPet方法及其實現的方法簽名是什么?
  • 選擇 1-1)
void addPet(std::unique_ptr<Pet> pet){
    pets.push_back(std::move(pet));
}
  • 選擇 1-2)
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);
  • 選擇 1-3)
void addPet(PetType pet){
    if(pet==PetType::Dog) pets.push_back(std::make_unique<Dog>());
    //
}
  1. 如果我選擇使用std::shared_ptr ```addPet''' 方法的方法簽名及其實現是什么?
  • 選擇 2-1)
void addPet(std::shared_ptr<Pet> pet){
    pets.push_back(std::move(pet));
}
  • 選擇 2-2)
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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM