C++基础补漏(四)

来源:互联网 发布:辐射 赵船长 知乎 编辑:程序博客网 时间:2024/06/14 14:12

这篇主要是C++的继承,多态


—————————————————————————————

20.多态性
所谓的多态就是在类里使用虚函数后,用父类作为对象指针,来正确的调用不同子类或父类来作为相应虚函数。虚基类实际就是继承时使用virtual关键字来定义,为的是让在多重继承时遇到相同的基类时只保留一份,以确定其使用那个类。
面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为。在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体。也可以这样说就是实现了“一个接口,多种方法”。

动态绑定:
动态绑定的概念和多态的概念感觉差不多= =
即使用基类的引用和指针调用一个虚函数,在程序运行时根据对象的不同进行不同的调用。




21.类的继承
概念:联系在一起的层次关系。继承的成为派生类(子类),被继承的成为基类(父类)。
            当B类继承与A类时,则自动的将父类中的函数和变量继承
语法如下:
class B : public A
{

}
类名后列出的是基类列表,即派生列表
派生访问说明符对派生类的成员及友元能否访问其直接基类的成员没什么关系,对于基类成员的访问权限只于激烈中的访问说明符有关。
只有子类公有的继承父类时,用户代码才可以使用派生类向基类的转换。


关于子类向父类类型转换重要:
子类对象内存上包含两部分:一部分是从父类的成员,另一个是子类自身的定义的成员。父类的所有成员都在子类的对象中,只是编译器限制了访问。
在概念模型上子类中含有父类对应的组成部分(在对象内存开始处),所以我们能把子类对象用父类对象的指针和引用使用。(这里可以看那个面向对象编程的概念的译文,上面讲的很详细)。

子类在构造时会调用父类的构造函数初始化父类的成员。我们需要指定初始化参数,否则会执行默认构造函数。
所以成员变量可以重复,在内存中存放不同的区域而已。

但是子类在调用父类成员是不需要使用指针或者成员访问符,就可直接访问(写了反而错),当子类成员和父类成员名字有相同是访问会访问子类的成员。同时编译器的名字的作用域是从派生类开始解析,如果没找到,在解析外层的基类。

*注:1.一个类不能派生自己。
          2.当一个类不想被继承时,可以在类名后加个final关键字。


22.虚拟继承
问题:如果Test没有加virtual关键字(此时编译器不会报错)
Parent *p = new Child( );
p->Test( );
虽然P指向的是对象的子类对象,但是应该调用父类的Test( )。并且构造和析构调用的也是父类
注:如果指针定义的是子对象,那就不存在这个问题

Virtual:(解决上述问题):会根据对象的实际类型,调用相应类型的函数
当一个成员函数需要子类重写,那么在父类应该将其声明为virtual
在子类中可直接覆盖父类的函数,运行时根据对象类型进行动态绑定。
virtrual本身表明该函数即将被重写(子类中可省略,因为他是继承的)


关于继承的注意事项:
1.加了virtual的成员函数一定要进行实现,否则会编译报错。继承他的类的成员函数也一样,因为virtual会被隐式继承。

2.当虚拟继承的成员函数和父类的那个成员函数,函数名相同,但形参列表不同时,编译器不会报错,但无法调用父类参数的成员函数。只能使用子类的那种参数的成员函数
   如果参数相同,返回值不同,编译器直接不同过。
  如果要放置错误,可以使用override和final的说明符。
 *注:这里的编译器通过是不包含成员函数调用的合理性。

3.加了virtual后用父类的指针,不影响父类对本身的调用。而是根据对象本身的类型,进行运行时绑定





23.继承的构造和析构函数
当一个子类对象被创建的时候,构造函数先调用父类的构造函数,在调用子类的构造函数
                                              析构函数被调用时先调用子类析构,在调用父类析构函数


当父类有多个构造函数,可以显性的调用其中的一个构造函数
注:构造函数不能加virtual,否则会编译器会报错

当一个类被继承时,应该将父类的析构函数函数声明为virtual,否则会有潜在问题:指针的静态类型与被删除对象的动态类型不符。
当用父类指针指向子类时,但超出作用域,调用的是父类的析构函数

内存问题:
类的成员函数不占内存,增加了virtual声明后,类的大小会有一点变化,按编译器变化,一般4个字节。




24.纯虚函数
概念:含有纯虚函数(不论多少个)的类称为抽象类(或者纯虚类),充当接口规范
接口规范:凡是准守此规范的类,都必须实现指定的函数接口,通常是一系列接口

语法:
(1).将成员函数声明为virtual
(2).后面加上 = 0 
(3).该函数没有函数体
例:
class CmdHandler
{
public: 
     virtual ~CmdHandler( );
     virtual void OnCommand(char* cmdline)= 0;
}

特点:
1.抽象类不能被实例化,即无法创造该对象
2.抽象类可以用来被多重继承
3.抽象类的析构函数应该声明为virtual。


2 0
原创粉丝点击