在c ++中的多态性
据我所知:
C ++提供了三种不同types的多态性。
- 虚拟function
- 函数名称重载
- 运算符重载
除上述三种多态性之外,还存在其他种类的多态性:
- 运行
- 编译时间
- 特别的多态性
- 参数多态性
我知道运行时多态性可以通过虚拟函数来实现, 静态多态性可以通过模板函数来实现
但是对于另外两个
- 特别的多态性
- 网站参数多态性说 ,
ad-hoc多态性:
如果可以使用的实际types的范围是有限的,并且在使用之前必须单独指定这些组合,这被称为ad-hoc多态性。
参数多态性:
如果所有代码都没有提及任何特定的types,因此可以透明地使用任何数量的新types,它被称为参数多态性。
我很难理解他们:(
任何人都可以用一个例子来解释它们吗? 我希望这个问题的答案能够帮助这些大学的许多新的辍学。
理解/要求多态性
为了理解多态性 – 正如“计算科学”中使用的术语一样,它有助于从简单的testing和定义开始。 考虑:
Type1 x; Type2 y; f(x); f(y);
在这里, f()
是执行一些操作,并被赋予值x
和y
作为input。
为了展现多态性,
f()
必须能够使用至less两个不同types的值(例如int
和double
)进行操作,find并执行不同types的代码。
C ++的多态机制
显式的程序员指定的多态性
你可以用下面的方法写f()
,使它可以在多种types上运行:
-
预处理:
#define f(X) ((X) += 2) // (note: in real code, use a longer uppercase name for a macro!)
-
重载:
void f(int& x) { x += 2; } void f(double& x) { x += 2; }
-
模板:
template <typename T> void f(T& x) { x += 2; }
-
虚拟发货:
struct Base { virtual Base& operator+=(int) = 0; }; struct X : Base { X(int n) : n_(n) { } X& operator+=(int n) { n_ += n; return *this; } int n_; }; struct Y : Base { Y(double n) : n_(n) { } Y& operator+=(int n) { n_ += n; return *this; } double n_; }; void f(Base& x) { x += 2; } // run-time polymorphic dispatch
其他相关机制
编译器为内置types,标准转换和强制转换提供的多态性将在后面讨论完整性:
- 无论如何,他们总是直观地理解(保证“ 哦,那个 ”反应)
- 它们影响了要求的门槛,以及上述机制的无缝使用
- 解释是从更重要的概念中分离出来的。
术语
进一步分类
考虑到上面的多态机制,我们可以用各种方法对它们进行分类:
-
什么时候select了多态特定types的代码?
- 运行时间意味着编译器必须为运行时程序可能处理的所有types生成代码,并在运行时select正确的代码( 虚拟调度 )
- 编译时间意味着在编译期间selecttypes特定的代码。 这样做的结果是:说一个程序只能用
int
参数调用上面的f
,这取决于所使用的多态机制和内联select,编译器可能会避免为f(double)
生成任何代码,或者生成的代码在编译的某个时候可能会被丢弃或链接。 ( 除虚拟调度以外的所有机制 )
-
支持哪些types?
- 特别的意思是你提供明确的代码来支持每种types(例如重载,模板特化); 你明确地增加了“为此”(按照特定的含义)types,其他一些“这个”,也许“那个”的支持。
-
参数意义,你可以尝试使用各种参数types的function,而不用特意做任何事情来支持它们(例如模板,macros)。 具有类似于模板/macros的函数/操作符的对象期望1 是所有该模板/macros需要完成其工作,而确切types是不相关的。 C ++ 11中的“概念”有助于expression和强化这样的期望 – 希望他们能够把它变成后来的标准。
-
参数多态性提供了鸭子打字 – 这个概念归功于詹姆斯·惠特科姆·莱利(James Whitcomb Riley),他显然说: “当我看到一只像鸭子一样走路的鸭子,像鸭子一样游泳,鸭子像鸭子一样游泳时,我把那只鸟叫做鸭子。 。
template <typename Duck> void do_ducky_stuff(const Duck& x) { x.walk().swim().quack(); } do_ducky_stuff(Vilified_Cygnet());
-
-
子types(又名包含)多态性允许您在不更新algorithm/函数的情况下处理新types,但它们必须从相同的基类(虚拟调度)
1 – 模板非常灵活。 SFINAE (另见std::enable_if
)有效地允许多组参数多态性的期望。 例如,当你正在处理的数据types有一个.size()
成员时,你可以使用一个函数,否则你可能会编码它,否则另一个函数不需要.size()
(但大概会以某种方式受到影响 -例如使用较慢的strlen()
或不打印日志中有用的消息)。 当模板用特定的参数实例化时,你也可以指定ad-hoc行为,或者保留一些参数参数( 部分模板特化)或者不完全特化 。
“多态”
Alf Steinbach评论说,在C ++标准中, 多态仅指使用虚拟调度的运行时多态。 一般比较 科学。 意思是更具包容性,按照C ++创build者Bjarne Stroustrup的词汇表( http://www.stroustrup.com/glossary.html ):
多态 – 为不同types的实体提供单一接口。 虚拟函数通过基类提供的接口提供dynamic(运行时)多态性。 重载的函数和模板提供了静态(编译时)多态性。 TC ++ PL 12.2.6,13.6.1,D&E 2.9。
这个答案 – 就像问题一样 – 将C ++特性与Comp相关联。 科学。 术语。
讨论
随着C ++标准使用比“多态”更窄的定义。 科学。 社区,以确保您的观众相互理解考虑… … –
- 使用明确的术语(“我们可以使这个代码可重复使用的其他types?”或“我们可以使用虚拟调度吗?”而不是“我们可以使这个代码多态吗?”),和/或
- 明确定义你的术语。
尽pipe如此,成为一名优秀的C ++程序员至关重要的是理解什么多态性真的在为你做…
让你一次编写“algorithm”代码,然后将其应用于许多types的数据
…然后非常了解不同的多态机制如何符合您的实际需求。
运行时多态性适合:
- input由工厂方法处理并作为通过
Base*
s处理的异构对象集合吐出, - 在运行时根据configuration文件,命令行开关,UI设置等select的实现,
- 实现在运行时变化,例如状态机模式。
当运行时多态性没有明确的驱动程序时,编译时选项通常更可取。 考虑:
- 在模拟类中编译什么的方面比在运行时失败的胖接口更可取
- SFINAE
- CRTP
- 优化(包括内联和死代码消除,循环展开,基于堆栈的静态数组和堆)
-
__FILE__
,__FILE__
__LINE__
,string连接和macros的其他独特function(这仍然是邪恶的;-)) - 模板和macrostesting语义的使用是受支持的,但不会人为地限制如何提供支持(因为虚拟调度往往需要完全匹配的成员函数覆盖)
其他支持多态的机制
如所承诺的那样,为了完整性,涵盖了几个外围主题:
- 编译器提供的重载
- 转换
- 铸就/胁迫
这个答案最后讨论了如何结合使用来简化多态代码 – 特别是参数多态(模板和macros)。
映射到types特定操作的机制
>隐式编译器提供的重载
从概念上讲,编译器重载许多内置types的运算符。 这与用户指定的重载在概念上不同,但因为容易被忽略而被列出。 例如,您可以使用相同的符号x += 2
来添加到int
s和double
s,编译器会生成:
- types特定的CPU指令
- 相同types的结果。
重载然后无缝地扩展到用户定义的types:
std::string x; int y = 0; x += 'c'; y += 'c';
编译器为基本types提供的重载在高级(3GL +)计算机语言中很常见,对多态的明确讨论通常意味着更多的东西。 (2GLs – 汇编语言 – 通常要求程序员明确地使用不同的助记符为不同的types。)
>标准转换
C ++标准的第四部分描述了标准转换。
第一点是很好的总结(从一个旧的草案 – 希望仍然是基本上正确的):
-1-标准转换是为内置types定义的隐式转换。 条款列举了全套这样的转换。 标准转换序列是按以下顺序的标准转换序列:
-
从以下集合中进行零或一个转换:左值到右值的转换,数组到指针的转换以及函数到指针的转换。
-
从以下集合中进行零或一个转换:整数升级,浮点升级,积分转换,浮点转换,浮点积分转换,指针转换,指向成员转换的指针以及布尔转换。
-
零或一个资格转换。
[注意:标准转换序列可以是空的,即它可以不包含转换。 ]如果需要,将标准转换序列应用于expression式,以将其转换为所需的目标types。
这些转换允许代码,如:
double a(double x) { return x + 2; } a(3.14); a(42);
应用较早的testing:
为了是多态,[
a()
]必须能够使用至less两个不同types的值(例如int
和double
)来查找和执行types适当的代码 。
a()
本身专门为double
运行代码,因此不是多态的。
但是,在第二次调用a()
,编译器知道为“浮点提升”(标准§4)生成types适当的代码,将42
转换为42.0
。 额外的代码在调用函数中。 我们将在结论中讨论这个意义。
>强制,强制转换,隐式构造函数
这些机制允许用户定义的类指定类似于内置types的标准转换的行为。 我们来看一下:
int a, b; if (std::cin >> a >> b) f(a, b);
在这里,对象std::cin
在布尔上下文中,在转换运算符的帮助下计算。 这可以在概念上与上述标准转换中的“整体促销”等进行分组。
隐式构造函数有效地执行相同的操作,但是由转换types来控制:
f(const std::string& x); f("hello"); // invokes `std::string::string(const char*)`
编译器提供的超载,转换和强制的影响
考虑:
void f() { typedef int Amount; Amount x = 13; x /= 2; std::cout << x * 1.1; }
如果我们希望在分割过程中x
的数量被视为一个实数(例如6.5而不是四舍五入到6),那么我们只需要改变typedef double Amount
。
这很好,但是明确地使代码“input正确”并不是太多工作:
void f() void f() { { typedef int Amount; typedef double Amount; Amount x = 13; Amount x = 13.0; x /= 2; x /= 2.0; std::cout << double(x) * 1.1; std::cout << x * 1.1; } }
但是,考虑到我们可以将第一个版本转换为template
:
template <typename Amount> void f() { Amount x = 13; x /= 2; std::cout << x * 1.1; }
这是由于这些“便利function”,它可以轻松实例化为int
或double
,并按预期工作。 没有这些function,我们需要明确的强制转换,types特征和/或策略类,一些冗长的,容易出错的混乱,如:
template <typename Amount, typename Policy> void f() { Amount x = Policy::thirteen; x /= static_cast<Amount>(2); std::cout << traits<Amount>::to_double(x) * 1.1; }
因此,编译器为内置types提供了运算符重载,标准转换,转换/强制/隐式构造函数 – 它们都为多态性提供了微妙的支持。 从这个答案顶部的定义,他们通过映射寻址“查找并执行types适当的代码”:
-
“离开”参数types
-
来自许多数据types的多态algorithm代码句柄
-
为(可能较less)数量(相同或其他)types编写的代码。
-
-
从“常量types”的值到“参数types”
它们本身并不build立多态的上下文,但是有助于在这样的上下文中授权/简化代码。
你可能会觉得被骗了,看起来并不多。 其意义在于,在参数多态上下文中(即在模板或macros内部),我们试图支持任意大的范围types,但通常希望在其他函数,文字和操作上expression操作,小套types。 当操作/值在逻辑上相同时,减less了在每种types的基础上创build几乎相同的function或数据的需要。 这些function合作增加了“尽力而为”的态度,通过使用有限的可用function和数据来做直观上的预期,并且在存在真正的模糊性时仅停止错误。
这有助于限制对支持多态代码的多态代码的需求,在多态性的使用周围形成一个更紧密的networking,因此本地化的使用不会强制广泛使用,并且可以根据需要提供多态性的好处,而不必强加必须在编译时间,在对象代码中具有相同逻辑函数的多个副本以支持所使用的types,并且在进行虚拟调度而不是内联或至less编译时parsing的调用。 正如在C ++中所典型的那样,程序员有很大的自由来控制使用多态的边界。
在C ++中,重要的区别是运行时与编译时绑定。 Ad-hoc与参数并没有什么帮助,稍后我会解释。
|----------------------+--------------| | Form | Resolved at | |----------------------+--------------| | function overloading | compile-time | | operator overloading | compile-time | | templates | compile-time | | virtual methods | run-time | |----------------------+--------------|
注 – 运行时多态性可能仍然在编译时解决,但这只是优化。 需要有效地支持运行时解决scheme,并与其他问题进行交易,这是导致虚拟function成为现实的一部分。 这对于C ++中所有forms的多态来说都非常重要,每种forms都是在不同的环境中进行不同的权衡。
函数重载和运算符重载在每个重要的方面都是一样的。 名称和使用它们的语法不会影响多态性。
模板允许您一次指定大量的函数重载。
还有一组同名决议时间的想法…
|---------------+--------------| | early binding | compile-time | | late binding | run-time | |---------------+--------------|
这些名称与OOP更相关,所以说模板或其他非成员函数使用早期绑定有点奇怪。
为了更好地理解虚函数和函数重载之间的关系,了解“单发”和“多发”的区别也很有用。 这个想法可以理解为一个进展…
- 首先是单态函数。 函数的实现由函数名唯一标识。 没有一个参数是特殊的。
- 然后,有单个派遣。 其中一个参数被认为是特殊的,并且使用(和名字一起)来确定使用哪个实现。 在OOP中,我们倾向于把这个参数看作“对象”,把它列在函数名之前
- 然后,有多个派遣。 任何/所有参数都有助于识别使用哪个实现。 因此,再一次,没有一个参数需要特别的。
OOP显然更多的是提名一个参数为特殊的借口,但这只是其中的一部分。 关于我所说的关于权衡的问题 – 单个调度很容易高效(通常的实现称为“虚拟表”)。 多次调度比较尴尬,不仅仅是效率方面,也是单独编译。 如果你好奇,你可能会查找“expression问题”。
正如对非成员函数使用术语“早期绑定”有点奇怪,在编译时解决多态性的术语“单一分派”和“多次分派”是有点奇怪的。 通常,C ++被认为没有多次调度,这被认为是一种特殊的运行时间分辨率。 但是,函数重载可以看作是在编译时进行的多重调度。
回到参数对特定的多态性,这些术语在函数式编程中更受欢迎,而且它们在C ++中不太适用。 即使是这样…
参数多态意味着你有types作为参数,并且使用完全相同的代码,而不pipe你用于这些参数的types。
即席多态是临时的,因为您根据特定的types提供不同的代码。
重载和虚函数都是ad-hoc多态的例子。
再次,有一些同义词…
|------------+---------------| | parametric | unconstrained | | ad-hoc | constrained | |------------+---------------|
除了这些并不完全是同义词,尽pipe它们通常被当作是对待的,而这正是C ++中可能出现混淆的地方。
将这些视为同义词的原因是通过将多态性约束到特定types的types,可以使用特定于这些typestypes的操作。 这里的“类”这个词可以在OOP的意义上解释,但是实际上只是指(共同指定)共享某些操作的types集合。
所以通常采用参数多态(至less默认)来暗示无约束多态。 因为无论types参数如何都使用相同的代码,唯一可支持的操作是适用于所有types的操作。 通过使这些types不受限制,可以严格限制可以应用于这些types的操作集。
在例如哈斯克尔,你可以有…
myfunc1 :: Bool -> a -> a -> a myfunc1 cxy = if c then x else y
这里是一个无约束的多态types。 这可能是任何事情,所以我们不能用这种types的值做什么。
myfunc2 :: Num a => a -> a myfunc2 x = x + 3
在这里, a
被限制为Num
类的成员 – 类似数字的types。 这个约束允许你用这些值来做数字事情,比如添加它们。 即使是多态的types推断也可以指出你的意思是typesa
的3
。
我认为这是受限的参数多态性。 只有一个实现,但只能在受限的情况下应用。 特别的方面是select使用哪个+
和3
。 Num
每个“实例”都有自己独特的实现。 所以即使在Haskell中,“parametric”和“unconstrained”也不是真正的同义词 – 不要怪我,这不是我的错!
在C ++中,重载和虚函数都是ad-hoc多态。 ad-hoc多态的定义并不关心在运行时还是编译时select实现。
如果每个模板参数都具有typestypename
C ++就会非常接近具有模板的参数多态性。 有types参数,无论使用哪种types,都有一个实现。 然而,“replace失败不是错误”规则意味着由于在模板内使用操作而出现隐式约束。 其他复杂性包括模板专门化提供替代模板 – 不同的(临时)实现。
所以在某种程度上C ++有参数多态性,但是它隐含地受到限制,可能被特殊的替代方法所覆盖 – 也就是说,这种分类对于C ++来说并不适用。
对于ad-hoc多态,意味着函数重载或者操作符重载。 看看这里:
http://en.wikipedia.org/wiki/Ad-hoc_polymorphism
至于参数多态性,模板函数也可以被计入,因为它们不一定需要参数为FIXEDtypes。 例如,一个函数可以对整数数组进行sorting,还可以对string数组进行sorting等。
这可能没有任何帮助,但我做了这个介绍我的朋友编程,通过发出定义的function,如START
, END
为主function,所以它不是太艰巨(他们只使用main.cpp文件)。 它包含多态类和结构,模板,向量,数组,预处理器指令,友谊,操作符和指针(在尝试多态性之前,您应该知道所有这些):
注意:它没有完成,但是你可以得到这个想法
main.cpp中
#include "main.h" #define ON_ERROR_CLEAR_SCREEN false START Library MyLibrary; Book MyBook("My Book", "Me"); MyBook.Summarize(); MyBook += "Hello World"; MyBook += "HI"; MyBook.EditAuthor("Joe"); MyBook.EditName("Hello Book"); MyBook.Summarize(); FixedBookCollection<FairyTale> FBooks("Fairytale Books"); FairyTale MyTale("Tale", "Joe"); FBooks += MyTale; BookCollection E("E"); MyLibrary += E; MyLibrary += FBooks; MyLibrary.Summarize(); MyLibrary -= FBooks; MyLibrary.Summarize(); FixedSizeBookCollection<5> Collection("My Fixed Size Collection"); /* Extension Work */ Book* Duplicate = MyLibrary.DuplicateBook(&MyBook); /* Extension Work */ Duplicate->Summarize(); END
main.h
#include <iostream> #include <sstream> #include <vector> #include <string> #include <type_traits> #include <array> #ifndef __cplusplus #error Not C++ #endif #define START int main(void)try{ #define END GET_ENTER_EXIT return(0);}catch(const std::exception& e){if(ON_ERROR_CLEAR_SCREEN){system("cls");}std::cerr << "Error: " << e.what() << std::endl; GET_ENTER_EXIT return (1);} #define GET_ENTER_EXIT std::cout << "Press enter to exit" << std::endl; getchar(); class Book; class Library; typedef std::vector<const Book*> Books; bool sContains(const std::string s, const char c){ return (s.find(c) != std::string::npos); } bool approve(std::string s){ return (!sContains(s, '#') && !sContains(s, '%') && !sContains(s, '~')); } template <class C> bool isBook(){ return (typeid(C) == typeid(Book) || std::is_base_of<Book, C>()); } template<class ClassToDuplicate> class DuplicatableClass{ public: ClassToDuplicate* Duplicate(ClassToDuplicate ToDuplicate){ return new ClassToDuplicate(ToDuplicate); } }; class Book : private DuplicatableClass<Book>{ friend class Library; friend struct BookCollection; public: Book(const char* Name, const char* Author) : name_(Name), author_(Author){} void operator+=(const char* Page){ pages_.push_back(Page); } void EditAuthor(const char* AuthorName){ if(approve(AuthorName)){ author_ = AuthorName; } else{ std::ostringstream errorMessage; errorMessage << "The author of the book " << name_ << " could not be changed as it was not approved"; throw std::exception(errorMessage.str().c_str()); } } void EditName(const char* Name){ if(approve(Name)){ name_ = Name; } else{ std::ostringstream errorMessage; errorMessage << "The name of the book " << name_ << " could not be changed as it was not approved"; throw std::exception(errorMessage.str().c_str()); } } virtual void Summarize(){ std::cout << "Book called " << name_ << "; written by " << author_ << ". Contains " << pages_.size() << ((pages_.size() == 1) ? " page:" : ((pages_.size() > 0) ? " pages:" : " pages")) << std::endl; if(pages_.size() > 0){ ListPages(std::cout); } } private: std::vector<const char*> pages_; const char* name_; const char* author_; void ListPages(std::ostream& output){ for(int i = 0; i < pages_.size(); ++i){ output << pages_[i] << std::endl; } } }; class FairyTale : public Book{ public: FairyTale(const char* Name, const char* Author) : Book(Name, Author){} }; struct BookCollection{ friend class Library; BookCollection(const char* Name) : name_(Name){} virtual void operator+=(const Book& Book)try{ Collection.push_back(&Book); }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } virtual void operator-=(const Book& Book){ for(int i = 0; i < Collection.size(); ++i){ if(Collection[i] == &Book){ Collection.erase(Collection.begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } private: const char* name_; Books Collection; }; template<class FixedType> struct FixedBookCollection : public BookCollection{ FixedBookCollection(const char* Name) : BookCollection(Name){ if(!isBook<FixedType>()){ std::ostringstream errorMessage; errorMessage << "The type " << typeid(FixedType).name() << " cannot be initialized as a FixedBookCollection"; throw std::exception(errorMessage.str().c_str()); delete this; } } void operator+=(const FixedType& Book)try{ Collection.push_back(&Book); }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } void operator-=(const FixedType& Book){ for(int i = 0; i < Collection.size(); ++i){ if(Collection[i] == &Book){ Collection.erase(Collection.begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } private: std::vector<const FixedType*> Collection; }; template<size_t Size> struct FixedSizeBookCollection : private std::array<const Book*, Size>{ FixedSizeBookCollection(const char* Name) : name_(Name){ if(Size < 1){ throw std::exception("A fixed size book collection cannot be smaller than 1"); currentPos = 0; } } void operator+=(const Book& Book)try{ if(currentPos + 1 > Size){ std::ostringstream errorMessage; errorMessage << "The FixedSizeBookCollection " << name_ << "'s size capacity has been overfilled"; throw std::exception(errorMessage.str().c_str()); } this->at(currentPos++) = &Book; }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } private: const char* name_; int currentPos; }; class Library : private std::vector<const BookCollection*>{ public: void operator+=(const BookCollection& Collection){ for(int i = 0; i < size(); ++i){ if((*this)[i] == &Collection){ std::ostringstream errorMessage; errorMessage << "The BookCollection " << Collection.name_ << " was already in the library, and therefore cannot be added"; throw std::exception(errorMessage.str().c_str()); } } push_back(&Collection); } void operator-=(const BookCollection& Collection){ for(int i = 0; i < size(); ++i){ if((*this)[i] == &Collection){ erase(begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The BookCollection " << Collection.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } Book* DuplicateBook(Book* Book)const{ return (Book->Duplicate(*Book)); } void Summarize(){ std::cout << "Library, containing " << size() << ((size() == 1) ? " book collection:" : ((size() > 0) ? " book collections:" : " book collections")) << std::endl; if(size() > 0){ for(int i = 0; i < size(); ++i){ std::cout << (*this)[i]->name_ << std::endl; } } } };
这里是使用Polymorphic类的一个基本例子
#include <iostream> class Animal{ public: Animal(const char* Name) : name_(Name){/* Add any method you would like to perform here*/ virtual void Speak(){ std::cout << "I am an animal called " << name_ << std::endl; } const char* name_; }; class Dog : public Animal{ public: Dog(const char* Name) : Animal(Name) {/*...*/} void Speak(){ std::cout << "I am a dog called " << name_ << std::endl; } }; int main(void){ Animal Bob("Bob"); Dog Steve("Steve"); Bob.Speak(); Steve.Speak(); //return (0); }
多态性意味着许多forms,因此它被用于操作员在不同情况下的不同行为。 多态用于实现inheritance。 对于前者,我们已经定义了一个类的形状fn draw(),然后绘制fn可以实现绘制圆,框,三angular形和其他形状。 (这是类形状的对象)
如果有人对这些人说CUT
The Surgeon The Hair Stylist The Actor
会发生什么?
The Surgeon would begin to make an incision. The Hair Stylist would begin to cut someone's hair. The Actor would abruptly stop acting out of the current scene, awaiting directorial guidance.
So above representation shows What is polymorphism (same name, different behavior) in OOP.
If you are going for an interview and interviewer asks you tell/show a live example for polymorphism in the same room we are sitting at, say-
Answer – Door / Windows
Wondering How?
Through Door / Window – a person can come, air can come, light can come, rain can come, etc.
ie One form different behavior(Polymorphism).
To understand it better and in a simple manner I used above example.. If you need reference for code follow above answers.