简体   繁体   English

使用智能指针制作多态容器的正确方法是什么?

[英]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.提前致谢。

  1. Should I use 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_ptrstd::shared_ptr or should I go back to using raw pointers?还是我应该 go 回到使用原始指针?
  2. If I choose to use std::unique_ptr what would be the method signature of addPet method and its implementation?如果我选择使用std::unique_ptr addPet方法及其实现的方法签名是什么?
  • choice 1-1)选择 1-1)
void addPet(std::unique_ptr<Pet> pet){
    pets.push_back(std::move(pet));
}
  • choice 1-2)选择 1-2)
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);
  • choice 1-3)选择 1-3)
void addPet(PetType pet){
    if(pet==PetType::Dog) pets.push_back(std::make_unique<Dog>());
    //
}
  1. If I choose to use std::shared_ptr what would be the method signature of ```addPet''' method and its implementation?如果我选择使用std::shared_ptr ```addPet''' 方法的方法签名及其实现是什么?
  • choice 2-1)选择 2-1)
void addPet(std::shared_ptr<Pet> pet){
    pets.push_back(std::move(pet));
}
  • choice 2-2)选择 2-2)
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后提供的参数是否仍然有效。

Here 'sa great post about the subject. 是一篇关于这个主题的好文章。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM