当我从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 ++接口的?)
答案 0 :(得分:7)
您的方案的典型解决方案将涉及资源管理处理程序对象,您按传递值。热门候选人是shared_ptr
和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
}
如果可行,您也可以将AnimalPtr
定义为std::unique_ptr<Animal>
,但之后您必须说addAnimal(std::move(a));
。这是更具限制性的(因为在任何给定时间只有一个物体处理动物),但也更轻。
答案 1 :(得分:1)
当你处理多态时,你会想要直接使用指向类而不是类的指针。这源于静态和动态类型之间的差异。如果你有:
void AddAnimal(Animal animal) { /* blah */ }
在“blah”中,对象动物同时具有静态和动态类型的动物,这意味着它只是动物,而只是动物。相反,如果你拿一个指针:
void AddAnimal(Animal *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等等。