[英]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.