C++中如何实现“虚拟模板功能”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5871722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to achieve "virtual template function" in C++
提问by Shadow
first off: I have read and I know now that a virtual template member function is not (yet?) possible in C++. A workaround would be to make the class a template and then use the template-argument also in the member-function.
首先:我已经阅读过并且现在我知道虚拟模板成员函数(还?)在 C++ 中是不可能的。一种解决方法是使类成为模板,然后也在成员函数中使用模板参数。
But in the context of OOP, I find that the below example would not be very "natural" if the class was actually a template. Please note that the code is actually not working, but the gcc-4.3.4 reports: error: templates may not be ‘virtual'
但是在 OOP 的上下文中,如果类实际上是模板,我发现下面的示例不会很“自然”。请注意,代码实际上不起作用,但 gcc-4.3.4 报告:error: templates may not be ‘virtual'
#include <iostream>
#include <vector>
class Animal {
public:
template< class AMOUNT >
virtual void eat( AMOUNT amount ) const {
std::cout << "I eat like a generic Animal." << std::endl;
}
virtual ~Animal() {
}
};
class Wolf : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a wolf!" << std::endl;
}
virtual ~Wolf() {
}
};
class Fish : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a fish!" << std::endl;
}
virtual ~Fish() {
}
};
class GoldFish : public Fish {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a goldfish!" << std::endl;
}
virtual ~GoldFish() {
}
};
class OtherAnimal : public Animal {
virtual ~OtherAnimal() {
}
};
int main() {
std::vector<Animal*> animals;
animals.push_back(new Animal());
animals.push_back(new Wolf());
animals.push_back(new Fish());
animals.push_back(new GoldFish());
animals.push_back(new OtherAnimal());
for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
(*it)->eat();
delete *it;
}
return 0;
}
So creating a "Fish< Amount > foo" is kind of strange. However, it seems desirable to me to provide an arbitrary amount of food to eat for each animal.
所以创建一个 "Fish< Amount > foo" 有点奇怪。然而,我似乎希望为每只动物提供任意数量的食物。
Thus, I am searching a solution about how to achieve something like
因此,我正在寻找有关如何实现类似目标的解决方案
Fish bar;
bar.eat( SomeAmount food );
This becomes particularly useful when looking at the for-loop. One might like to feed a specific amount (FoodAmount) to all of the different animals (via eat() and bind1st() e.g.), it could not be done that easily, although I wound find this very inuitive (and thus to some extent "natural). While some might want to argue now that this is due to the "uniform"-character of a vector, I think/wish that it should be possible to achieve this and I really would like to know how, as this is puzzling me for quite some time now...
这在查看 for 循环时变得特别有用。一个人可能想给所有不同的动物喂食特定量(FoodAmount)(例如通过eat() 和bind1st()),这不容易做到,尽管我觉得这非常直观(因此在某种程度上“自然。虽然现在有些人可能想争辩说这是由于向量的“统一”特征,我认为/希望应该有可能实现这一点,我真的很想知道如何,因为这是困惑我好久了...
[EDIT]
[编辑]
To perhaps clarify the motivation behind my question, I want to program an Exporter-class and let different, more specialized classes derive from it. While the top-level Exporter-class is generally only for cosmetic/structural purpose, a GraphExporter-class is derived, that should again serve as a base-class for even more specialzed export. However, similar to the Animal-example, I would like to be able to define GraphExporter* even on specialized/derived classes (e.g. on SpecialGraphExplorer) but when calling "write( out_file )", it should call the appropriate member function for SpecialGraphExporter instead of GraphExporter::write( out_file).
为了澄清我的问题背后的动机,我想编写一个 Exporter 类,并让不同的、更专业的类从中派生。虽然顶级 Exporter-class 通常仅用于装饰/结构目的,但 GraphExporter-class 是派生的,它应该再次用作更专业导出的基类。但是,与 Animal-example 类似,我希望即使在专用/派生类(例如在 SpecialGraphExplorer 上)也能够定义 GraphExporter*,但是在调用“write(out_file)”时,它应该为 SpecialGraphExporter 调用适当的成员函数GraphExporter::write(out_file)。
Maybe this makes my situation and intentions clearer.
也许这让我的处境和意图更加清晰。
Best,
最好的事物,
Shadow
阴影
回答by sehe
After some thinking I recognized this as the classic multi-methodrequirement, i.e. a method that dispatches based on the runtime typeof more than one parameter. Usual virtual functions are single dispatch
in comparison (and they dispatch on the type of this
only).
经过一番思考,我认识到这是经典的多方法需求,即一种基于多个参数的运行时类型进行调度的方法。通常的虚函数进行single dispatch
比较(并且它们this
仅根据类型调度)。
Refer to the following:
请参阅以下内容:
- Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
- Chapter 11: "Multimethods"- it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
- A codeproject articlethat seems to have just such an implementation:
- no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
- no use of RTTI;
- no use of preprocessor;
- strong type safety;
- separate compilation;
- constant time of multimethod execution;
- no dynamic memory allocation (via new or malloc) during multimethod call;
- no use of nonstandard libraries;
- only standard C++ features is used.
- C++ Open Method Compiler, Peter Pirkelbauer, Yuriy Solodkyy, and Bjarne Stroustrup
- The Loki Library has A MultipleDispatcher
- Wikipedia has quite a nice simple write-upwith examples on Multiple Dispatch in C++.
- Andrei Alexandrescu 撰写了关于在“现代 C++ 设计”中使用泛型实现多方法的文章(C++ 的开创性部分?)
- 一个CodeProject上的文章,似乎刚才这样的实现:
- 不使用任何类型的类型转换(动态、静态、重新解释、const 或 C 风格)
- 不使用 RTTI;
- 不使用预处理器;
- 强类型安全;
- 单独编译;
- 多方法执行时间恒定;
- 在多方法调用期间没有动态内存分配(通过 new 或 malloc);
- 不使用非标准库;
- 仅使用标准 C++ 功能。
- C++ 开放方法编译器、Peter Pirkelbauer、Yuriy Solodkyy 和 Bjarne Stroustrup
- Loki 库有一个 MultipleDispatcher
- Wikipedia 有一个相当不错的简单文章,其中包含有关 C++ 中 Multiple Dispatch 的示例。
Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):
以下是维基百科文章中的“简单”方法以供参考(对于大量派生类型,不太简单的方法可以更好地扩展):
// Example using run time type comparison via dynamic_cast
struct Thing {
virtual void collideWith(Thing& other) = 0;
}
struct Asteroid : Thing {
void collideWith(Thing& other) {
// dynamic_cast to a pointer type returns NULL if the cast fails
// (dynamic_cast to a reference type would throw an exception on failure)
if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
// handle Asteroid-Asteroid collision
} else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
// handle Asteroid-Spaceship collision
} else {
// default collision handling here
}
}
}
struct Spaceship : Thing {
void collideWith(Thing& other) {
if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
// handle Spaceship-Asteroid collision
} else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
// handle Spaceship-Spaceship collision
} else {
// default collision handling here
}
}
}
回答by Mikael Persson
Obviously, virtual member function templates are not allowed and could not be realized even theoretically. To build a base class' virtual table, there needs to be a finite number of virtual function-pointer entries. A function template would admit an indefinite amount of "overloads" (i.e. instantiations).
显然,虚拟成员函数模板是不允许的,甚至在理论上也无法实现。要构建基类的虚拟表,需要有有限数量的虚拟函数指针条目。函数模板将允许无限量的“重载”(即实例化)。
Theoretically-speaking, a language (like C++) could allow virtual member function templates if it had some mechanism to specify the actual (finite) list of instantiations. C++ does have that mechanism (i.e. explicit template instantiations), so I guess it could be possible to do this in a newer C++ standard (although I have no idea what trouble it would entail for compiler vendors to implement this feature). But, that's just a theoretical discussion, in practice, this is simply not allowed. The fact remains, you have to make the number of virtual functions finite (no templates allowed).
从理论上讲,如果一种语言(如 C++)具有某种机制来指定实际(有限)实例化列表,则它可以允许虚拟成员函数模板。C++ 确实具有这种机制(即显式模板实例化),因此我想可以在较新的 C++ 标准中执行此操作(尽管我不知道编译器供应商实现此功能会带来什么麻烦)。但是,这只是理论上的讨论,在实践中,这是根本不允许的。事实仍然存在,您必须使虚函数的数量有限(不允许使用模板)。
Of course, that doesn't mean that class template cannot have virtual functions, nor does it mean that virtual functions cannot call function templates. So, there are many solutions in that vein (like the Visitor pattern or other schemes).
当然,这并不意味着类模板不能有虚函数,也不意味着虚函数不能调用函数模板。因此,在这种情况下有许多解决方案(如访问者模式或其他方案)。
One solution that, I think, serves your purpose (although it is hard to comprehend) elegantly is the following (which is basically a visitor pattern):
我认为,优雅地满足您的目的(尽管很难理解)的一种解决方案如下(这基本上是访问者模式):
#include <iostream>
#include <vector>
struct Eater {
virtual void operator()(int amount) const = 0;
virtual void operator()(double amount) const = 0;
};
template <typename EaterType>
struct Eater_impl : Eater {
EaterType& data;
Eater_impl(EaterType& aData) : data(aData) { };
virtual void operator()(int amount) const { data.eat_impl(amount); };
virtual void operator()(double amount) const { data.eat_impl(amount); };
};
class Animal {
protected:
Animal(Eater& aEat) : eat(aEat) { };
public:
Eater& eat;
virtual ~Animal() { delete &eat; };
};
class Wolf : public Animal {
private:
template< class AMOUNT >
void eat_impl( AMOUNT amount) const {
std::cout << "I eat like a wolf!" << std::endl;
}
public:
friend struct Eater_impl<Wolf>;
Wolf() : Animal(*(new Eater_impl<Wolf>(*this))) { };
virtual ~Wolf() { };
};
class Fish : public Animal {
private:
template< class AMOUNT >
void eat_impl( AMOUNT amount) const {
std::cout << "I eat like a fish!" << std::endl;
}
public:
friend struct Eater_impl<Fish>;
Fish() : Animal(*(new Eater_impl<Fish>(*this))) { };
virtual ~Fish() { };
};
int main() {
std::vector<Animal*> animals;
animals.push_back(new Wolf());
animals.push_back(new Fish());
for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
(*it)->eat(int(0));
(*it)->eat(double(0.0));
delete *it;
};
return 0;
};
The above is a neat solution because it allows you to define a finite number of overloads that you want in one place only (in the Eater_impl class template) and all you need in the derived class is a function template (and possibly additional overloads, for special cases). There is, of course, a bit of overhead, but I guess that a bit more thought could be put into it to reduce the overhead (additional reference storage and dynamic allocation of Eater_impl). I guess, the curiously recurring template pattern could probably be employed somehow to this end.
上面是一个巧妙的解决方案,因为它允许您仅在一个地方(在 Eater_impl 类模板中)定义有限数量的重载,并且您在派生类中需要的只是一个函数模板(可能还有额外的重载,例如特别案例)。当然,有一点开销,但我想可以多考虑一下以减少开销(额外的引用存储和 Eater_impl 的动态分配)。我想,奇怪的重复模板模式可能会以某种方式用于此目的。
回答by Industrial-antidepressant
I think the visitor pattern can be a solution.
我认为访问者模式可以是一个解决方案。
UPDATE
更新
I finished my example:
我完成了我的例子:
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
class Animal;
class Wolf;
class Fish;
class Visitor
{
public:
virtual void visit(const Animal& p_animal) const = 0;
virtual void visit(const Wolf& p_animal) const = 0;
virtual void visit(const Fish& p_animal) const = 0;
};
template<class AMOUNT>
class AmountVisitor : public Visitor
{
public:
AmountVisitor(AMOUNT p_amount) : m_amount(p_amount) {}
virtual void visit(const Animal& p_animal) const
{
std::cout << "I eat like a generic Animal." << std::endl;
}
virtual void visit(const Wolf& p_animal) const
{
std::cout << "I eat like a wolf!" << std::endl;
}
virtual void visit(const Fish& p_animal) const
{
std::cout << "I eat like a fish!" << std::endl;
}
AMOUNT m_amount;
};
class Animal {
public:
virtual void Accept(const Visitor& p_visitor) const
{
p_visitor.visit(*this);
}
virtual ~Animal() {
}
};
class Wolf : public Animal {
public:
virtual void Accept(const Visitor& p_visitor) const
{
p_visitor.visit(*this);
}
};
class Fish : public Animal {
public:
virtual void Accept(const Visitor& p_visitor) const
{
p_visitor.visit(*this);
}
};
int main()
{
typedef boost::shared_ptr<Animal> TAnimal;
std::vector<TAnimal> animals;
animals.push_back(TAnimal(new Animal()));
animals.push_back(TAnimal(new Wolf()));
animals.push_back(TAnimal(new Fish()));
AmountVisitor<int> amount(10);
for (std::vector<TAnimal>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
(*it)->Accept(amount);
}
return 0;
}
this prints:
这打印:
I eat like a generic Animal.
I eat like a wolf!
I eat like a fish!
回答by eacousineau
Per Mikael's post, I have made another offshoot, using the CRTP and following Eigen's style of using derived()
for an explicit subclass reference:
根据 Mikael 的帖子,我做了另一个分支,使用 CRTP 并遵循 Eigenderived()
用于显式子类引用的风格:
// Adaptation of Visitor Pattern / CRTP from:
// http://stackoverflow.com/a/5872633/170413
#include <iostream>
using std::cout;
using std::endl;
class Base {
public:
virtual void tpl(int x) = 0;
virtual void tpl(double x) = 0;
};
// Generics for display
template<typename T>
struct trait {
static inline const char* name() { return "T"; }
};
template<>
struct trait<int> {
static inline const char* name() { return "int"; }
};
template<>
struct trait<double> {
static inline const char* name() { return "double"; }
};
// Use CRTP for dispatch
// Also specify base type to allow for multiple generations
template<typename BaseType, typename DerivedType>
class BaseImpl : public BaseType {
public:
void tpl(int x) override {
derived()->tpl_impl(x);
}
void tpl(double x) override {
derived()->tpl_impl(x);
}
private:
// Eigen-style
inline DerivedType* derived() {
return static_cast<DerivedType*>(this);
}
inline const DerivedType* derived() const {
return static_cast<const DerivedType*>(this);
}
};
// Have Child extend indirectly from Base
class Child : public BaseImpl<Base, Child> {
protected:
friend class BaseImpl<Base, Child>;
template<typename T>
void tpl_impl(T x) {
cout << "Child::tpl_impl<" << trait<T>::name() << ">(" << x << ")" << endl;
}
};
// Have SubChild extend indirectly from Child
class SubChild : public BaseImpl<Child, SubChild> {
protected:
friend class BaseImpl<Child, SubChild>;
template<typename T>
void tpl_impl(T x) {
cout << "SubChild::tpl_impl<" << trait<T>::name() << ">(" << x << ")" << endl;
}
};
template<typename BaseType>
void example(BaseType *p) {
p->tpl(2);
p->tpl(3.0);
}
int main() {
Child c;
SubChild sc;
// Polymorphism works for Base as base type
example<Base>(&c);
example<Base>(&sc);
// Polymorphism works for Child as base type
example<Child>(&sc);
return 0;
}
Output:
输出:
Child::tpl_impl<int>(2)
Child::tpl_impl<double>(3)
SubChild::tpl_impl<int>(2)
SubChild::tpl_impl<double>(3)
SubChild::tpl_impl<int>(2)
SubChild::tpl_impl<double>(3)
This snippet may be found in source here: repro:c808ef0:cpp_quick/virtual_template.cc
这个片段可以在这里的源代码中找到:repro:c808ef0:cpp_quick/virtual_template.cc
回答by AJG85
Virtual template function is not allowed. However you can use one OR the other here.
不允许使用虚拟模板功能。但是,您可以在此处使用一个或另一个。
You could make an interface using virtual methods and implement your various animals in terms of having an eating interface. (i.e. PIMPL)
您可以使用虚拟方法制作一个界面,并根据饮食界面来实现您的各种动物。(即 PIMPL)
Less human intuitive would be having a non-member non-friend template function as a free function which could take templated const reference to any animal and make them eat accordingly.
将非成员非友元模板函数作为一个自由函数,可以将模板化的 const 引用用于任何动物,并让它们相应地进食。
For the record you don't need templates here. Pure virtual abstract method on the base class is enough to force and interface where all animals must eat and define how they do so with an override, providing a regular virtual would be enough to say all animals can eat but if they don't have a specific way then they can use this default way.
为了记录,您在这里不需要模板。基类上的纯虚拟抽象方法足以强制和接口所有动物必须进食并定义它们如何使用覆盖来定义它们,提供常规虚拟就足以说明所有动物都可以吃,但如果它们没有具体方式那么他们可以使用这种默认方式。
回答by user3103989
You can create a template class with virtual function, and implement the function in the derived class without using template in the follwing way:
您可以创建一个带有虚函数的模板类,并在派生类中实现该函数,而无需使用模板,方法如下:
a.h:
template <class T>
class A
{
public:
A() { qDebug() << "a"; }
virtual A* Func(T _template) { return new A;}
};
b.h:
class B : public A<int>
{
public:
B();
virtual A* Func(int _template) { return new B;}
};
and the function CTOR and call
A<int>* a1=new B;
int x=1;
a1->Func(x);
unfortunately i havn't found a way to create a virtual function with template parameters without declaring the class as a template and it template type on the dervied class
不幸的是,我还没有找到一种方法来创建带有模板参数的虚函数而不将该类声明为模板,并且它在派生类上的模板类型
回答by user3103989
I have copied your code and modified it, so now it should work exactly as you want:
我已经复制了你的代码并修改了它,所以现在它应该完全按照你的意愿工作:
#include <iostream>
#include <vector>
//defined new enum type
enum AnimalEnum
{
animal,
wolf,
fish,
goldfish,
other
};
//forward declarations
class Wolf;
class Fish;
class GoldFish;
class OtherAnimal;
class Animal {
private:
AnimalEnum who_really_am_I;
void* animal_ptr;
public:
//declared new constructors overloads for each type of animal
Animal(const Animal&);
Animal(const Wolf&);
Animal(const Fish&);
Animal(const GoldFish&);
Animal(const OtherAnimal&);
template< class AMOUNT >
/*removed the virtual keyword*/ void eat( AMOUNT amount ) const {
switch (this->who_really_am_I)
{
case AnimalEnum::other: //You defined OtherAnimal so that it doesn't override the eat action, so it will uses it's Animal's eat
case AnimalEnum::animal: std::cout << "I eat like a generic Animal." << std::endl; break;
case AnimalEnum::wolf: ((Wolf*)this->animal_ptr)->eat(amount); break;
case AnimalEnum::fish: ((Fish*)this->animal_ptr)->eat(amount); break;
case AnimalEnum::goldfish: ((GoldFish*)this->animal_ptr)->eat(amount) break;
}
}
void DeleteMemory() { delete this->animal_ptr; }
virtual ~Animal() {
//there you can choose if whether or not to delete "animal_ptr" here if you want or not
}
};
class Wolf : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a wolf!" << std::endl;
}
virtual ~Wolf() {
}
};
class Fish : public Animal {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a fish!" << std::endl;
}
virtual ~Fish() {
}
};
class GoldFish : public Fish {
public:
template< class AMOUNT >
void eat( AMOUNT amount) const {
std::cout << "I eat like a goldfish!" << std::endl;
}
virtual ~GoldFish() {
}
};
class OtherAnimal : public Animal {
//OtherAnimal constructors must be defined here as Animal's constructors
OtherAnimal(const Animal& a) : Animal(a) {}
OtherAnimal(const Wolf& w) : Animal(w) {}
OtherAnimal(const Fish& f) : Animal(f) {}
OtherAnimal(const GoldFish& g) : Animal(g) {}
OtherAnimal(const OtherAnimal& o) : Animal(o) {}
virtual ~OtherAnimal() {
}
};
//OtherAnimal will be useful only if it has it's own actions and members, because if not, typedef Animal OtherAnimal or using OtherAnimal = Animal can be used, and it can be removed from above declarations and below definitions
//Here are the definitions of Animal constructors that were declared above/before:
Animal::Animal(const Animal& a) : who_really_am_I(AnimalEnum::animal), animal_ptr(nullptr) {}
Animal::Animal(const Wolf& w) : who_really_am_I(AnimalEnum::wolf), animal_ptr(new Wolf(w)) {}
Animal::Animal(const Fish& f) : who_really_am_I(AnimalEnum::fish), animal_ptr(new Fish(f)) {}
Animal::Animal(const GoldFish& g) : who_really_am_I(AnimalEnum::goldfish), animal_ptr(new GoldFish(g)) {}
Animal::Animal(const OtherAnimal& o) :
who_really_am_I(AnimalEnum::other), animal_ptr(new OtherAnimal(o)) {}
int main() {
std::vector<Animal> animals;
animals.push_back(Animal());
animals.push_back(Wolf()); //Wolf is converted to Animal via constructor
animals.push_back(Fish()); //Fish is converted to Animal via constructor
animals.push_back(GoldFish()); //GoldFish is converted to Animal via constructor
animals.push_back(OtherAnimal()); //OtherAnimal is converted to Animal via constructor
for (std::vector<Animal>::const_iterator it = animals.begin(); it != animals.end(); ++it) {
it->eat(); //this is Animal's eat that invokes other animals eat
//delete *it; Now it should be:
it->DeleteMemory();
}
animals.clear(); //All animals have been killed, and we don't want full vector of dead animals.
return 0;
}
回答by nate
In you scenario, you are trying to mix compile time polymorphism with runtime polymorphism, but it cannot be done in this "direction".
在您的场景中,您试图将编译时多态性与运行时多态性混合使用,但不能在这个“方向”上完成。
Essential, your AMOUNT template argument represents an expected interface for the type to implement based on the union of all the operations each implementation of eat uses. If you where to create an abstract type that declared each of those operations making them virtual where needed, then you could call eat with different types (that derived from your AMOUNT interface). And it would behave as expected.
本质上,您的 AMOUNT 模板参数表示要实现的类型的预期接口,该接口基于每个eat 实现使用的所有操作的联合。如果您在哪里创建一个抽象类型,声明每个操作使它们在需要的地方成为虚拟的,那么您可以使用不同的类型(从您的 AMOUNT 接口派生的)调用eat。它会按预期运行。
回答by umlcat
I don't work with templates, but I think:
我不使用模板,但我认为:
(1) You cannot use templates inside a class, templates are more like global types or global variables.
(1) 不能在类中使用模板,模板更像是全局类型或全局变量。
(2) In O.O.P., the same problem you present, and that you are trying to solve by using templates, can be solved by using inheritance.
(2) 在 OOP 中,你提出的同样的问题,你试图通过使用模板来解决,可以通过使用继承来解决。
Classes work similar to templates, you can extended by adding new things, or replace things of classes with pointers, pointers to objects (A.K.A. "references") and overriding virtual functions.
类的工作方式类似于模板,您可以通过添加新事物进行扩展,或者用指针、指向对象的指针(又名“引用”)和覆盖虚函数来替换类的事物。
#include <iostream>
struct Animal {
virtual void eat(int amount ) {
std::cout << "I eat like a generic Animal." << std::endl;
}
virtual ~Animal() { }
};
#if 0
// example 1
struct Wolf : Animal {
virtual void eat(int amount) {
std::cout << "I eat like a wolf!" << std::endl;
}
};
struct Fish : Animal {
virtual void eat(int amount) {
std::cout << "I eat like a fish!" << std::endl;
}
};
#else
// example 2
struct AnimalFood {
virtual int readAmount() { return 5; }
virtual void showName() {
std::cout << "I'm generic animal food" << std::endl;
}
};
struct PredatorFood : AnimalFood {
virtual int readAmount() { return 500; }
virtual void showName() {
std::cout << "I'm food for a predator" << std::endl;
}
};
struct Fish : Animal {
virtual void eat(AnimalFood* aFood) {
if (aFood->readAmount() < 50) {
std::cout << "OK food, vitamines: " << aFood->readAmount() << std::endl;
} else {
std::cout << "too much food, vitamines: " << aFood->readAmount() << std::endl;
}
}
};
struct Shark : Fish {
virtual void eat(AnimalFood* aFood) {
if (aFood->readAmount() < 250) {
std::cout << "too litle food for a shark, Im very hungry, vitamines: " << aFood->readAmount() << std::endl;
} else {
std::cout << "OK, vitamines: " << aFood->readAmount() << std::endl;
}
}
};
struct Wolf : Fish {
virtual void eat(AnimalFood* aFood) {
if (aFood->readAmount() < 150) {
std::cout << "too litle food for a wolf, Im very hungry, vitamines: " << aFood->readAmount() << std::endl;
} else {
std::cout << "OK, vitamines: " << aFood->readAmount() << std::endl;
}
}
};
#endif
int main() {
// find animals
Wolf* loneWolf = new Wolf();
Fish* goldenFish = new Fish();
Shark* sharky = new Shark();
// prepare food
AnimalFood* genericFood = new AnimalFood();
PredatorFood* bigAnimalFood = new PredatorFood();
// give food to animals
loneWolf->eat(genericFood);
loneWolf->eat(bigAnimalFood);
goldenFish->eat(genericFood);
goldenFish->eat(bigAnimalFood);
sharky->eat(genericFood);
sharky->eat(bigAnimalFood);
delete bigAnimalFood;
delete genericFood;
delete sharky;
delete goldenFish;
delete loneWolf;
}
Cheers.
干杯。