3

当我从 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++ 接口进行编程?)

4

2 回答 2

7

您的场景的典型解决方案将涉及您通过值传递的资源管理处理程序对象。受欢迎的候选人是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
}

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

于 2011-11-08T23:54:00.923 回答
1

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

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

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

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 的使用方式等。

于 2011-11-09T00:02:49.463 回答