[英]What is the correct way to make a polymorphic container using smart pointers?
I would like to make a C++ class that maintains a polymorphic container.我想做一个维护多态容器的 C++ class 。 For example, a vet can hold a list of pets currently receiving treatment.
例如,兽医可以持有一份目前正在接受治疗的宠物的清单。 If we use raw pointers, we could define a vet as follows.
如果我们使用原始指针,我们可以如下定义一个 vet。
class Vet{
std::vector<Pet*> pets;
public:
void addPet(Pet* pet);
};
We could add pets as follows.我们可以如下添加宠物。
Vet vet;
vet.addPet(new Dog{});
vet.addPet(new Cat{});
In this case, the destructor of Vet class should be made responsible for deleting the dynamically allocated pets maintained in pets
.在这种情况下,应该让 Vet class 的析构函数负责删除 pets 中维护的动态分配的
pets
。 To avoid this, I would like to use smart pointers.为了避免这种情况,我想使用智能指针。 However, there are some issues which need clarification before I can correctly and cleanly implement such code.
但是,在我能够正确和干净地实现此类代码之前,需要澄清一些问题。 Need help on following issues.
在以下问题上需要帮助。 Thanks in advance.
提前致谢。
std::unique_ptr
or std::shared_ptr
?std::unique_ptr
还是std::shared_ptr
? or in which circumstances should I use std::unique_ptr
or std::shared_ptr
?std::unique_ptr
或std::shared_ptr
? or should I go back to using raw pointers?std::unique_ptr
what would be the method signature of addPet
method and its implementation?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));
}
This choice only works if I construct a pet as follows.仅当我按以下方式构建宠物时,此选择才有效。
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
what would be the method signature of ```addPet''' method and its implementation?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);
}
Assuming you really want to create the pets on the outside, the best way forward would be unique_ptr
and passing it by value, like below:假设你真的想在外面创建宠物,最好的方法是
unique_ptr
并按值传递它,如下所示:
void addPet(std::unique_ptr<Pet> pet);
unique_ptr
because you're telling readers that there is only a single owner. unique_ptr
因为您告诉读者只有一个所有者。
By value, because that would require you to either use a temporary or explicitly move the unique_ptr when calling addPet
.按价值计算,因为这需要您在调用
addPet
时使用临时或显式移动 unique_ptr 。 Making it very explicit on the outside that you're moving/transferring ownership.在外部非常明确地表明您正在移动/转移所有权。
Eg you could either do例如,您可以这样做
auto pet = std::make_unique<Dog>();
vet.addPet(std::move(pet)); // move is required, making it explicit that ownership is transferred
or或者
vet.addPet(std::make_unique<Dog>());
If you would accept a reference to a unique_ptr
in addPet
the caller would not know whether the parameter that is provided is still valid after the call to addPet
or not.如果您在
addPet
中接受对unique_ptr
的引用,则调用者将不知道在调用addPet
后提供的参数是否仍然有效。
Use unique_ptr
.使用
unique_ptr
。 shared_ptr
is for sharing ownership, but you want the vet
to own the pets it contains, ie no ownership is shared. shared_ptr
用于共享所有权,但您希望vet
拥有它包含的宠物,即不共享所有权。
The unique_ptr
needs not be present at the interface. unique_ptr
不需要出现在界面上。 The public interface to add cats and dogs can be this:添加猫狗的公共界面可以是这样的:
class vet {
public:
void add_dog(const std::string& name);
void add_cat(const std::string& name);
};
Use unique_ptr
as function argument when you want to transfer ownership.当您想要转移所有权时,使用
unique_ptr
作为 function 参数。 There is no need to first let the caller own the pets and then let them transfer ownership to the vet
....unless thats what you want, then do use std::unique_ptr<Pet>
as argument (no reference, but by value).没有必要先让调用者拥有宠物,然后让他们将所有权转移给
vet
....除非那是你想要的,然后使用std::unique_ptr<Pet>
作为参数(没有参考,但按价值)。
If not necessary don't implement resource control that is already available.如果没有必要,不要实施已经可用的资源控制。 You don't have to have the user construct the object, necessarily, which adds the benefit that you don't expose to the user what data structures you use internally.
您不必让用户构建 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)
Note however, that semantically, a Vet does not own a pet, they take care of it for a while and release the pet afterwards.但是请注意,从语义上讲,兽医并不拥有宠物,他们会照顾它一段时间,然后释放宠物。 A vet that destroys a pet after they're done treating it would be a bad vet.
兽医在完成治疗后摧毁宠物将是一个糟糕的兽医。
Anyhow, if you want access to a pet, you can easily add a similar member function:无论如何,如果您想获得宠物,您可以轻松添加类似的成员 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.