提问人:Vijay 提问时间:5/2/2011 最后编辑:CœurVijay 更新时间:5/7/2021 访问量:58816
C++ 中的多态性
Polymorphism in C++
问:
AFAIK:
C++ 提供了三种不同类型的多态性。
- 虚拟功能
- 函数名称重载
- 运算符重载
除了上述三种多态性外,还存在其他类型的多态性:
- 运行时
- 编译时
- 临时多态性
- 参数多态性
我知道运行时多态可以通过虚函数实现,静态多态可以通过模板函数实现
但对于其他两个
- 临时多态性
- 参数多态性 该网站说,
临时多态性:
如果可以使用的实际类型的范围是有限的,并且必须在使用前单独指定组合,则这称为临时多态性。
参数多态性:
如果所有代码在编写时都未提及任何特定类型,因此可以透明地与任意数量的新类型一起使用,则称为参数多态性。
我几乎无法理解他们:(
如果可能的话,任何人都可以用一个例子来解释它们吗? 我希望这个问题的答案对他们大学的许多新毕业生有所帮助。
答:
至于临时多态性,它意味着函数重载或算子重载。在这里查看:
http://en.wikipedia.org/wiki/Ad-hoc_polymorphism
至于参数多态性,模板函数也可以算在内,因为它们不一定接受 FIXED 类型的参数。例如,一个函数可以对整数数组进行排序,也可以对字符串数组进行排序,等等。
http://en.wikipedia.org/wiki/Parametric_polymorphism
评论
<
Ord
<
Ord
对多态性的理解/要求
要理解多态性 - 正如该术语在计算机科学中使用的术语一样 - 它有助于从简单的测试和定义开始。考虑:
Type1 x;
Type2 y;
f(x);
f(y);
在这里,是执行一些操作,并被赋予值并作为输入。f()
x
y
要表现出多态性,必须能够使用至少两种不同类型的值进行操作(例如 和 ),查找并执行不同的适合类型的代码。
f()
int
double
多态性的 C++ 机制
显式程序员指定的多态性
您可以编写这样一种方式,以便它可以通过以下任一方式对多种类型进行操作:f()
预处理:
#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
其他相关机制
编译器为内置类型提供的多态性、标准转换和强制转换/强制,为了完整性,稍后将讨论如下:
- 无论如何,它们通常都是凭直觉理解的(保证“哦,那个”反应),
- 它们会影响要求上述机制的门槛和无缝使用,以及
- 解释是对更重要概念的繁琐分心。
术语
进一步分类
鉴于上述多态性机制,我们可以以各种方式对它们进行分类:
何时选择特定于多态类型的代码?
- 运行时意味着编译器必须为程序在运行时可能处理的所有类型生成代码,并在运行时选择正确的代码(虚拟调度)
- 编译时是指在编译过程中选择特定于类型的代码。这样做的结果是:假设一个程序只在上面用参数调用——根据所使用的多态机制和内联选项,编译器可能会避免生成任何代码,或者生成的代码可能会在编译或链接的某个时候被丢弃。(除虚拟调度外,上述所有机制
f
int
f(double)
)
支持哪些类型?
- 临时意味着您提供显式代码来支持每种类型(例如重载、模板专用化);您明确添加了支持“为此”(根据临时的含义)类型,其他一些“this”,也许还有“that”;-)。
参数化意味着您可以尝试将该函数用于各种参数类型,而无需专门执行任何操作来启用其对它们的支持(例如模板、宏)。具有类似于模板/宏期望的函数/运算符的对象1 是模板/宏完成其工作所需的全部内容,确切的类型无关紧要。C++20 引入的“概念”表达并强制执行了这种期望 - 请参阅此处的 cppreference 页面。
参数化多态性提供了鸭子类型 - 这个概念归因于詹姆斯·惠特科姆·莱利(James Whitcomb Riley),他显然说过:“当我看到一只鸟像鸭子一样走路,像鸭子一样游泳,像鸭子一样嘎嘎叫时,我称这只鸟为鸭子。
template <typename Duck> void do_ducky_stuff(const Duck& x) { x.walk().swim().quack(); } do_ducky_stuff(Vilified_Cygnet());
子类型(又名包含)多态性允许您在不更新算法/函数的情况下处理新类型,但它们必须派生自相同的基类(虚拟调度)
1 - 模板非常灵活。SFINAE(另见 std::enable_if
)有效地允许对参数多态性进行多组期望。例如,您可以编码为,当您正在处理的数据类型具有成员时,您将使用一个函数,否则将使用另一个不需要的函数(但可能会以某种方式受到影响 - 例如,使用较慢的函数或不打印日志中有用的消息)。您还可以在使用特定参数实例化模板时指定临时行为,将某些参数参数化(部分模板专用化)保留,或者不保留(完全专用化)。.size()
.size()
strlen()
“多态性”
Alf Steinbach评论说,在C++标准中,多态性仅指使用虚拟调度的运行时多态性。根据 C++ 的创建者 Bjarne Stroustrup 的词汇表(http://www.stroustrup.com/glossary.html),General Comp. Sci. 的含义更具包容性:
多态性 - 为不同类型的实体提供单一接口。虚拟函数通过基类提供的接口提供动态(运行时)多态性。重载函数和模板提供静态(编译时)多态性。TC++PL 12.2.6、13.6.1、D&E 2.9。
这个答案 - 就像问题一样 - 将 C++ 功能与 Comp. Sci. 术语相关联。
讨论
由于 C++ 标准使用的“多态性”定义比 Comp. Sci. 社区更窄,为了确保您的受众相互理解,请考虑...
- 使用明确的术语(“我们能否使此代码可用于其他类型?”或“我们可以使用虚拟调度?”而不是“我们可以使此代码多态化吗?”),和/或
- 明确定义您的术语。
尽管如此,要成为一名优秀的 C++ 程序员,最重要的是了解多态性真正为你做了什么......
让您编写一次“算法”代码,然后将其应用于多种类型的数据
...然后要非常清楚不同的多态机制如何匹配您的实际需求。
运行时多态性适合:
- 输入由工厂方法处理,并作为通过 S 处理的异构对象集合吐出,
Base*
- 在运行时根据配置文件、命令行开关、UI 设置等选择实现,
- 实现在运行时各不相同,例如状态机模式。
当运行时多态性没有明确的驱动程序时,编译时选项通常更可取。考虑:
- 模板化类的编译方面比在运行时失败的胖接口更可取
- SFINAE (英语:SFINAE)
- CRTP系列
- 优化(许多包括内联和死代码消除、循环展开、基于静态堆栈的数组与堆)
__FILE__
,,字符串文字连接和宏的其他独特功能(仍然是邪恶的;-))__LINE__
- 支持模板和宏测试语义用法,但不要人为地限制该支持的提供方式(因为虚拟调度往往通过要求完全匹配的成员函数重写)
支持多态性的其他机制
正如所承诺的,为了完整起见,涵盖了几个外围主题:
- 编译器提供的重载
- 转换
- 施放/胁迫
最后,本文将讨论上述内容如何结合在一起,以增强和简化多态代码,尤其是参数多态性(模板和宏)。
映射到特定于类型的操作的机制
> 隐式编译器提供的重载
从概念上讲,编译器会重载内置类型的许多运算符。它在概念上与用户指定的重载没有区别,但被列出来是因为它很容易被忽略。例如,您可以使用相同的表示法向 s 和 s 相加,编译器会生成:int
double
x += 2
- 特定于类型的 CPU 指令
- 相同类型的结果。
然后,重载无缝扩展到用户定义的类型:
std::string x;
int y = 0;
x += 'c';
y += 'c';
编译器为基本类型提供的重载在高级 (3GL+) 计算机语言中很常见,对多态性的显式讨论通常意味着更多内容。(2GL - 汇编语言 - 通常要求程序员为不同的类型显式使用不同的助记符。
> 标准转换
C++ 标准的第四部分描述了标准转换。
第一点很好地总结了(来自旧草案 - 希望仍然基本正确):
-1- 标准转换是为内置类型定义的隐式转换。子句 conv 枚举了此类转换的完整集合。标准转换序列是按以下顺序排列的标准转换序列:
从以下集合中进行零次或一次转换:左值到右值的转换、数组到指针的转换和函数到指针的转换。
来自以下集合的零次或一次转换:整数升级、浮点升级、整数转换、浮点转换、浮点整型转换、指针转换、指向成员的指针转换和布尔值转换。
零个或一个资格转换。
[注意:标准转换序列可以是空的,即它可以不包含任何转换。如有必要,将对表达式应用标准转换序列,以将其转换为所需的目标类型。
这些转换允许如下代码:
double a(double x) { return x + 2; }
a(3.14);
a(42);
应用前面的测试:
要实现多态性,[] 必须能够使用至少两种不同类型的值进行操作(例如 和 ),查找并执行适合类型的代码。
a()
int
double
a()
本身专门运行代码,因此不是多态的。double
但是,在对编译器的第二次调用中,知道为“浮点提升”(标准§4)生成适合类型的代码,以转换为.该额外代码位于调用函数中。我们将在结论中讨论这一点的重要性。a()
42
42.0
> 强制、强制转换、隐式构造函数
这些机制允许用户定义的类指定类似于内置类型的标准转换的行为。让我们来看看:
int a, b;
if (std::cin >> a >> b)
f(a, b);
在这里,在转换运算符的帮助下,在布尔上下文中对对象进行计算。这在概念上可以归类为上述主题中标准转换中的“整体促销”等。std::cin
隐式构造函数有效地执行相同的操作,但由强制转换类型控制:
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;
}
如果我们希望在除法过程中将金额视为实数(即 6.5 而不是四舍五入为 6),我们只需要更改为 .x
typedef double Amount
这很好,但要使代码显式“类型正确”不会太多:
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;
}
正是由于这些小小的“便利功能”,它才能如此轻松地为或或实例化,并按预期工作。如果没有这些功能,我们将需要显式强制转换、类型特征和/或策略类,以及一些冗长、容易出错的混乱,例如:int
double
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;
}
因此,编译器为内置类型提供的运算符重载、标准转换、强制转换/强制/隐式构造函数 - 它们都为多态性提供了微妙的支持。从这个答案顶部的定义来看,他们通过映射来解决“查找和执行适合类型的代码”:
“远离”参数类型
从多种数据类型中多态算法代码句柄
为(可能较少)数量的(相同或其他)类型编写代码。
“to”来自常量类型值的参数类型
它们本身不会建立多态上下文,但确实有助于在此类上下文中授权/简化代码。
你可能会觉得被骗了......这似乎不多。重要的是,在参数化多态上下文中(即在模板或宏内部),我们试图支持任意大范围的类型,但通常希望根据为一小群类型设计的其他函数、文字和操作来表达对它们的操作。当操作/值在逻辑上相同时,它减少了在每个类型的基础上创建几乎相同的函数或数据的需要。这些功能共同增加了一种“尽力而为”的态度,通过使用有限的可用函数和数据来做直观预期的事情,并且只有在出现真正的歧义时才会因错误而停止。
这有助于限制对支持多态代码的多态代码的需求,围绕多态的使用绘制更紧密的网,以便本地化使用不会强制广泛使用,并根据需要提供多态性的好处,而不会产生必须在编译时公开实现的成本,在目标代码中具有同一逻辑函数的多个副本以支持使用的类型, 以及执行虚拟调度,而不是内联或至少在编译时解析调用。正如 C++ 中的典型情况一样,程序员有很大的自由来控制使用多态性的边界。
评论
dynamic_cast
在 C++ 中,重要的区别是运行时与编译时绑定。临时与参数化并没有真正的帮助,我稍后会解释。
|----------------------+--------------|
| Form | Resolved at |
|----------------------+--------------|
| function overloading | compile-time |
| operator overloading | compile-time |
| templates | compile-time |
| virtual methods | run-time |
|----------------------+--------------|
注意 - 运行时多态性可能仍可在编译时解决,但这只是优化。需要有效地支持运行时解析,并权衡其他问题,是导致虚拟功能成为现实的部分原因。这对于 C++ 中所有形式的多态性都非常关键——每种多态性都源于在不同上下文中做出的不同权衡。
函数重载和运算符重载在各个方面都是一回事。名称和使用它们的语法不会影响多态性。
模板允许您一次指定许多函数重载。
对于相同的解决时间想法,还有另一组名称......
|---------------+--------------|
| early binding | compile-time |
| late binding | run-time |
|---------------+--------------|
这些名称与 OOP 的关联性更强,因此说模板或其他非成员函数使用早期绑定有点奇怪。
为了更好地理解虚拟函数和函数重载之间的关系,了解“单次调度”和“多重调度”之间的区别也很有用。这个想法可以理解为一种进步......
- 首先,有单态函数。函数的实现由函数名称唯一标识。没有一个参数是特殊的。
- 然后,有单一的调度。其中一个参数被视为特殊参数,并用于(与名称一起)标识要使用的实现。在 OOP 中,我们倾向于将此参数视为“对象”,将其列在函数名称等之前。
- 然后,有多个调度。任何/所有参数都有助于确定要使用的实现。因此,再一次,没有一个参数需要特殊。
显然,OOP 不仅仅是将一个参数指定为特殊参数的借口,但这只是其中的一部分。回到我所说的权衡 - 单次调度非常容易有效地完成(通常的实现称为“虚拟表”)。多重调度比较尴尬,不仅在效率上,而且对于单独编译也是如此。如果你很好奇,你可以查一下“表达问题”。
正如对非成员函数使用术语“早期绑定”有点奇怪一样,使用术语“单次调度”和“多重调度”在编译时解析多态性也有点奇怪。通常,C++ 被认为没有多重调度,这被认为是一种特殊的运行时解析。但是,函数重载可以看作是在编译时完成的多次调度。
回到参数化与临时多态性,这些术语在函数式编程中更受欢迎,它们在 C++ 中不太有效。虽然如此。。。
参数多态性意味着将类型作为参数,并且无论对这些参数使用哪种类型,都使用完全相同的代码。
即席多态性是即席的,因为您可以根据特定类型提供不同的代码。
重载和虚函数都是临时多态性的例子。
同样,有一些同义词......
|------------+---------------|
| parametric | unconstrained |
| ad-hoc | constrained |
|------------+---------------|
除了这些不是同义词,尽管它们通常被视为同义词,这就是 C++ 中可能出现混淆的地方。
将这些视为同义词的原因是,通过将多态性限制为特定的类型类,可以使用特定于这些类型类的操作。这里的“类”一词可以在 OOP 意义上解释,但实际上只是指(通常命名的)共享某些操作的类型集。
因此,参数多态性通常被(至少默认地)用于表示无约束的多态性。由于无论类型参数如何,都使用相同的代码,因此唯一可支持的操作是适用于所有类型的操作。通过使类型集不受约束,可以严格限制可应用于这些类型的操作集。
例如,在 Haskell 中,您可以拥有...
myfunc1 :: Bool -> a -> a -> a
myfunc1 c x y = if c then x else y
下面是一个不受约束的多态类型。它可以是任何东西,所以我们对这种类型的值无能为力。a
myfunc2 :: Num a => a -> a
myfunc2 x = x + 3
在这里,被限制为类的成员 - 行为类似于数字的类型。该约束允许您使用这些值执行数字操作,例如将它们相加。即使是 is 多态类型推断也得出了 .a
Num
3
3
a
我认为这是受约束的参数多态性。只有一种实现,但它只能应用于受约束的情况。临时方面是选择哪个和使用哪个。每个“实例”都有自己独特的实现。因此,即使在 Haskell 中,“参数化”和“不受约束”也不是真正的同义词——不要怪我,这不是我的错!+
3
Num
在 C++ 中,重载函数和虚函数都是临时多态性。即席多态性的定义并不关心是在运行时还是在编译时选择实现。
如果每个模板参数都有类型,则 C++ 非常接近模板的参数多态性。有类型参数,无论使用哪种类型,都有一个实现。但是,“替换失败不是错误”规则意味着由于在模板中使用操作而产生隐式约束。其他复杂性包括用于提供替代模板的模板专用化 - 不同的(临时)实现。typename
因此,在某种程度上,C++具有参数多态性,但它是隐式约束的,可以被临时替代方案覆盖 - 即这种分类并不真正适用于C++。
评论
a
下面是使用 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);
}
这可能没有任何帮助,但我这样做是为了通过给出定义的函数(例如 和 main 函数)来向我的朋友介绍编程,因此它不会太令人生畏(他们只使用 main.cpp 文件)。它包含多态类和结构、模板、向量、数组、preproccessor 指令、友谊、运算符和指针(在尝试多态性之前,您可能应该知道所有这些):START
END
注意:它还没有完成,但你可以得到这个想法
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;
}
}
}
};
如果有人对这些人说 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.
所以上面的表示显示了什么是OOP中的多态性(相同的名称,不同的行为)。
如果你要去面试,面试官要求你告诉我们/展示一个多态性的活生生的例子,在我们坐的同一个房间里,说——
答案 - 门/窗
想知道怎么做?
通过门/窗 - 一个人可以来,空气可以来,光线可以来,雨可以来,等等。
即一种形式不同的行为(多态性)。
为了更好地理解它,我以简单的方式使用了上面的例子。如果您需要代码参考,请按照上面的答案进行操作。
评论
多态性意味着许多形式,因此它用于运算符在不同实例下采取不同的行动。多态性用于实现继承。例如,我们已经为类形状定义了一个 fn draw(),然后 draw fn 可以用于绘制圆形、盒子、三角形和其他形状。( 是类形状的对象)
评论