c++对象参数:多态性、值语义、对象生命周期?

发布于 2024-12-14 09:34:19 字数 741 浏览 1 评论 0原文

当我从 C# 过渡到 C++ 时,我收到了很多关于尽可能使用值语义的建议。几乎可以保证,如果我在任何地方发布带有指针的问题,就会有人出现并建议它应该是一个值。我开始看到曙光,我在代码中发现了很多地方可以用堆栈分配的变量(通常是引用)替换动态分配和指针。因此,我认为当调用者中的对象生存期比被调用者中的对象生存期长时,我可以掌握使用堆栈分配的对象并将它们作为引用传递给其他函数。

但是,当被调用者取得所有权时,我有一个关于按值传递对象的问题。举个例子:

class Zoo
{
  void AddAnimal(Animal animal);
  std::list<Animal> animals_;
}

通常从灵活性和单元测试的角度来看,我希望 Animal 是一个接口(C++ 中的抽象类),这样我就可以轻松发送任意动物并用模拟实现来模拟它。

在指针实现中,客户端代码将这样调用:

Animal animal = new Lion("Bob");
myZoo.AddAnimal(animal);

这里客户端代码并不真正需要动物对象。它只是临时构建它以传递给该方法。所以在这种情况下,不存在共享语义。所以这似乎是价值语义的一个很好的例子。但是,我的理解是,您不能使用 Animal 作为按值传递的参数,因为它是一个抽象类。

我的大多数不采用基本类型的成员函数都采用抽象类参数。那么C++的处理这个问题的方法是什么呢? (这就是如何使用值语义对 C++ 接口进行编程?)

As I make the transition from C# to C++ I get a lot of recommendations to use value semantics where possible. It's pretty much guaranteed that if I post a question with a pointer anywhere someone will come along and suggest that it should be a value instead. I'm starting to see the light and I have found a lot of places in my code where I could replace dynamic allocation and pointers with stack allocated variables (and usually references). So I think I have a grasp on using stack allocated objects and passing them to other functions as references when the object lifetime is longer in the caller than the callee.

However I have a question about passing objects by value when the callee will take ownership. Take the following example:

class Zoo
{
  void AddAnimal(Animal animal);
  std::list<Animal> animals_;
}

Typically from a flexibility and unit testing perspective I'd want Animal to be an interface (abstract class in C++) so I can easily send arbitrary animals and mock it out with a mock implementation.

In a pointer implementation client code would be calling this like:

Animal animal = new Lion("Bob");
myZoo.AddAnimal(animal);

Here the client code doesn't really need the animal object. It's just constructing it temporarily to pass to the method. So in this case there aren't shared semantics. So it seems like a good case for value semantics. However, my understanding is that you can't use Animal as a parameter passed by value because it's an abstract class.

Most of my member functions that don't take primitive types take abstract class parameters. So what is the C++ method to handle this problem? (That is how do you program to interfaces in C++ with value semantics?)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

倾`听者〃 2024-12-21 09:34:19

您的场景的典型解决方案将涉及一个资源管理处理程序对象,您可以按值传递该对象。受欢迎的候选者是 shared_ptrunique_ptr

#include <list>
#include <memory>
#include "all_zoo_animals.h"  // yours

typedef std::shared_ptr<Animal> AnimalPtr;  // see note
typedef std::list<AnimalPtr> AnimalCollection;

AnimalCollection zoo;

void addAnimal(AnimalPtr a)
{
  zoo.push_back(a);
}

int main()
{
  AnimalPtr a = AnimalPtr(new Penguin);
  a.feed(fish);
  addAnimal(a);  // from local variable, see note

  addAnimal(AnimalPtr(new Puffin)); // from temporary
}

如果可行,您还可以将 AnimalPtr 定义为 std::unique_ptr< /code>,但是你必须说 addAnimal(std::move(a));。这更具限制性(因为在任何给定时间只有一个物体可以处理动物),但重量也更轻。

The typical solution for your scenario would involve a resource-managing handler object which you do pass by value. Popular candidates are shared_ptr and unique_ptr:

#include <list>
#include <memory>
#include "all_zoo_animals.h"  // yours

typedef std::shared_ptr<Animal> AnimalPtr;  // see note
typedef std::list<AnimalPtr> AnimalCollection;

AnimalCollection zoo;

void addAnimal(AnimalPtr a)
{
  zoo.push_back(a);
}

int main()
{
  AnimalPtr a = AnimalPtr(new Penguin);
  a.feed(fish);
  addAnimal(a);  // from local variable, see note

  addAnimal(AnimalPtr(new Puffin)); // from temporary
}

If it is feasible, you could also define AnimalPtr as std::unique_ptr<Animal>, but then you have to say addAnimal(std::move(a));. This is more restrictive (as only one object handles the animal at any given time), but also lighter-weight.

盗琴音 2024-12-21 09:34:19

当您处理多态性时,您将需要使用指向类的指针而不是直接使用类。这源于静态类型和动态类型之间的差异。如果你有:

void AddAnimal(Animal animal) { /* blah */ }

在“blah”中,对象动物有静态和动态类型的动物,这意味着它只是一个动物,而且只是一个动物。相反,如果您采用指针:

void AddAnimal(Animal *animal);

那么您就知道animal的静态类型,但它的动态类型可以自由变化,因此该函数可以采用/any/animal。

就我个人而言,我会使用以下三种调用约定之一:

class Zoo
{
  // This object takes ownership of the pointer:
  void AddAnimal(Animal* animal);
  std::list<shared_ptr<Animal>> animals_; (or boost::ptr_list)

  // This object shares ownership with other objects:
  void AddAnimal(shared_ptr<Animal> animal);
  std::list<shared_ptr<Animal>> animals_;

  // Caller retains ownership of the pointer:
  void AddAnimal(Animal* animal);
  std::list<Animal*> animals_;
}

取决于代码库的其余部分、Zoo 的使用方式等。

When you are dealing with polymorphism, you'll want to use pointers to a class instead of the class directly. This stems from the difference between static and dynamic types. If you have:

void AddAnimal(Animal animal) { /* blah */ }

Within "blah", the object animal has both a static and a dynamic type of Animal, meaning that is just an Animal, and only an Animal. If instead you take a pointer:

void AddAnimal(Animal *animal);

Then you know animal's static type, but it's dynamic type is free to vary, so the function can take /any/ animal.

Personally, I'd use one of the following three calling conventions:

class Zoo
{
  // This object takes ownership of the pointer:
  void AddAnimal(Animal* animal);
  std::list<shared_ptr<Animal>> animals_; (or boost::ptr_list)

  // This object shares ownership with other objects:
  void AddAnimal(shared_ptr<Animal> animal);
  std::list<shared_ptr<Animal>> animals_;

  // Caller retains ownership of the pointer:
  void AddAnimal(Animal* animal);
  std::list<Animal*> animals_;
}

Dependent on the rest of the codebase, how the Zoo would be used, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文