c++对象参数:多态性,值语义,对象生命周期
c++ Object parameters: polymorphism, value semantics, object lifetimes?
当我从c#过渡到c++时,我得到了很多建议,在可能的地方使用值语义。这几乎可以保证,如果我张贴一个问题与指针在任何地方有人会过来,并建议它应该是一个值,而不是。我开始看到光明,我发现在我的代码中有很多地方我可以用堆栈分配的变量(通常是引用)代替动态分配和指针。所以我认为我已经掌握了使用堆栈分配的对象,并在调用者中的对象生存期长于被调用者时将它们作为引用传递给其他函数。
然而,我有一个关于通过值传递对象时,被调用者将采取所有权的问题。看下面的例子:
class Zoo
{
void AddAnimal(Animal animal);
std::list<Animal> animals_;
}
通常从灵活性和单元测试的角度来看,我希望Animal是一个接口(c++中的抽象类),这样我就可以轻松地发送任意动物并使用模拟实现模拟它。
在指针实现中,客户端代码会这样调用:
Animal animal = new Lion("Bob");
myZoo.AddAnimal(animal);
这里的客户端代码并不真正需要动物对象。它只是临时构造它来传递给方法。所以在这种情况下,没有共享语义。这似乎是值语义的一个很好的例子。然而,我的理解是,你不能使用Animal作为一个参数传递值,因为它是一个抽象类。
大多数不接受基本类型的成员函数都接受抽象类形参。那么处理这个问题的c++方法是什么呢?
您的场景的典型解决方案将涉及一个资源管理处理程序对象,您按值传递该对象。热门候选是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));
。这是更严格的限制(因为在任何给定的时间只有一个对象处理动物),但也更轻量级。
在处理多态性时,您将希望使用指向类的指针,而不是直接指向类。这源于静态类型和动态类型之间的差异。如果你有:
void AddAnimal(Animal animal) { /* blah */ }
在"blah"中,对象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等
- 如何从具有移动语义的类对象中生成共享指针
- 了解函数返回对象的移动语义
- 使用非空函数对象提升精神语义动作
- 交换对象不实现移动语义时的交换函数
- 每次复制实现移动分配的非 const 对象时,我是否总是获得移动语义
- 用移动语义返回两个对象
- C++无效的对象返回语义
- C++11在返回本地对象时移动语义
- 返回本地对象是否需要移动语义
- 复制构造函数并通过跟踪对象移动语义
- 有没有办法使用 move 而不是复制语义将函数返回值(对象)包装在 Python 中
- 将对象作为函数的参数发送时移动语义
- 在共享库中全局声明的非pod对象的语义是什么?
- c++对象初始化和构造函数语义
- 移动大型非指针基对象的语义
- c++对象参数:多态性,值语义,对象生命周期
- 移动语义以避免临时对象的创建
- 关于对象初始化语义的问题
- 具有编译器强制对象唯一所有权语义的c++类
- 指针到函数和指针到对象语义