C++中的多态

以下为本人大一时阅读《C++ Primer Plus》中关于多态章节所做的笔记

多态:polymorphism

被调用的功能取决于用来调用函数的句柄(如指针或者引用)类型,而不是句柄所指向的对象类型。

例:A是基类,B是A的派生类

1.将基类指针指向派生类对象:(C++编译器允许这样的交叉赋值,因为每个派生类对象都是一个基类的对象)创建一个A类的指针指向B的一个对象,调用一个A和B都含有的函数,此时调用的是A类中的函数,而不是B类中的函数。同时将基类指针指向派生类的对象,只能访问在基类中拥有的成员函数,而不能访问派生类中的成员函数(句柄只能调用与它关联的类类型的成员函数)

2.将派生类指针指向基类指针:C++编译器不允许这样赋值,因为A的对象并不是B的对象(即基类对象并不是派生类的对象)

向下转换(downcasting):

显式地把基类指针强制转换为派生类指针,就允许通过指向派生类对象的基类指针访问只在派生类中拥有的成员。同时存在潜在风险。

virtual函数和virtual析构函数:

基类中的virtual函数在每个派生类中重写,从而导致多态性行为。

声明virtual函数,只需在该函数的原型前加上关键数virtual

通过基类指针或引用调用虚函数:

如果程序通过派生类对象的基类指针或者指向派生类对象的基类引用调用virtual函数,那么程序会根据所指对象的类型而不是指针类型,动态(执行时)选择正确的派生类draw函数。在执行时(不是编译时)选择合适的调用函数称为动态绑定或迟绑定。

例如:shapePtr->draw() 或 shapeRef.draw() (shapeRef是指向派生类对象的基类引用)

通过对象名称调用虚函数:

此时调用哪个函数在编译时就已经被决定了(称为静态绑定),所调用的virtual函数正是为该特定对象所属的类定义的函数,这并不是多态性行为。

例如:squareObject.draw()  (shapeObject是派生类对象)

virtual析构函数:

如果基类析构函数声明为virtual,那么任何派生类的析构函数都是virtual并重写基类的析构函数

virtual ~CommissionEmployee() {}

之后,对一个基类指针用delete来显式地删除它所指的类层次中的某个对象,那么系统会根据该指针所指对象调用相应类的析构函数。当一个派生类对象被销毁时,派生类对象中属于基类的部分也会被销毁,因此执行派生类和基类的析构函数是很重要的。基类的析构函数在派生类的析构函数执行后自动执行。

C+11:Final成员函数和类

基类的virtual函数在原型中声明为final

virtual someFunction( parameters ) final;

则该函数在任何派生类中不能被覆盖。

若将类声明为final可以防止该类被用作基类

class MyClass final

{

     …

};

视图重写一个final函数或者继承一个final基类会导致编译错误。

抽象类(abstract class):

不完整的类,不能用来实例化任何对象的类。因为通常抽象类在类的继承层次结构中作为基类,故称为抽象基类。其派生类必须在这些类的对象实例化前定义“缺少的部分”。

具体类(concrete class):

可以用来实例化对象的类

纯虚函数(pure virtual function):

在声明时初始化值为0

virtual void draw() const = 0;

"=0"称为纯指示符(pure specifier)。

virtual函数和纯virtual函数的区别:virtual函数有函数的实现,并且提供派生类是否重写这些函数的选择权;纯virtual没有函数的实现,需要派生类重写这些函数以使派生类成为具体类,否则派生类仍然是抽象类。

虽然我们不能实例化抽象基类的对象,但是可以是使用抽象基类来声明指向抽象类派生出的具体类对象的指针和引用。

如果一个函数在基类被声明为virtual,那么这个函数在整个类继承层次中都将保持virtual函数,在派生类中再在函数前加virtual关键字是多余的,但是为了使程序更加清晰可读,最好在类层次结构的每一级中都把这样的函数显式地声明为virtual函数。

如果virtual函数在派生类中未重写,则将继承基类中的函数。(纯virtual函数必须重写)

使用dynamic_cast决定对象类型:

设A是基类,B是派生类,ptr是一个A的指针(即A*类型),并且指向对象b。如果要判断对象b是否是B的类型,可以使用以下语句:

B* derivedPtr=dynamic_cast <B*>(ptr);

将ptr的类型动态向下强制转换为B*,如果这时候b是B类型,那么b的地址就赋给derivedPtr,如果b不是B类型,则derivedPtr为nullptr。最后可以用derivedPtr是否是nullptr来判断b是否是B类的对象。

(为什么需要这个过程?因为C++不允许程序把基类指针赋给派生类指针;试图使用基类指针来调用只有派生类中含有的函数是不允许的)

<typeinfo>:

type_info类的成员函数name返回一个基于指针的字符串,它包含传递给typeid实参的类型名称。运算符typeid返回一个type_info类对象的引用,包含了包括操作数类型名称在内的关于运算符操作数类型的信息。

例:(设A是一个类,ptr是指向一个A的对象的指针)

cout<<typeid(*ptr).name()<<end;

输出:class A

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值