浅谈继承和多态
来源:互联网 发布:赵薇事件公知 编辑:程序博客网 时间:2024/06/04 18:32
继承
继承可以使现有的代码具有可重用性和可扩展性。它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。其继承的过程,就是从一般到特殊的过程。
通过继承创建的新类称为”子类”或”派生类”。被继承的类称为”基类”、”父类”或”超类”.C++类继承通过三个方式来实现,包括:公有继承(public),保护继承(protected),私有继承(private),它们的不同在哪里呢?
分析:
1.基类的私有成员在派生类中不能被访问,如果一些基类成员不想被基类对象直接访问,但需要在派生类中能访问,就定义为保护成员,可以看出保护成员限定符是因继承才出现的
2.public继承是一个接口继承,每个父类可用的成员对子类也都是开放的,因此每个子类对象也都是一个父类对象
3.protected/private继承只是拥有基类的部分成员,因此在绝大多数情况下用的都是公有继承
4.无论何种继承方式,都可以访问基类的public成员和protect成员
为单继承和多继承
单继承:
class Base{public: void Print() {}private: int _b;};class Derived:public Base{public: void Print() {}private: int _d;}
多继承:
class Base1{public: void Print() {}private: int _b1;};class Base2{public: void Print() {}private: int _b2;};class Derived:public Base1,public Base2{public: void Print() {}private: int _d;}
继承中的默认成员函数
在C++中类有六个默认构造函数,非常重要,我们来重点来看一下构造和析构函数。
class Base{public: Base() { cout << "Base()" << endl; } ~Base() { cout << "~Base()" << endl; }private: int _b;};class Derived :public Base{public: Derived() { cout << "Derived()" << endl; } ~Derived() { cout << "~Derived()" << endl; }private: int _d;};void test(){ Derived d;}int main(){ test(); system("pause"); return 0;}
运行结果为:
Base()
Derived()
~Derived()
~Base()
由此得出,在创建子类对象是同时调用了子类和父类的构造函数,并且父类的构造函数先于子类构造函数,析构函数也同样调用了子类和父类。多继承同样如此,只是调用顺序需要按照继承顺序。
菱形继承
菱形继承就是通常所说的钻石继承
C1和C2都继承了Base,D又继承了C1和C2,这样继承有一个问题那就是数据冗余,C1里面有Base,C2里面也有Base,在D这里相当于有两份Base,并且当根据D所创建的对象,访问Base里的成员时候,存在二义性,是访问C1中的还是C2中的呢,为了解决这种多重继承提出了虚继承。
虚继承
虚继承是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。
具体我们来看代码
1.菱形继承(类中有虚函数)
#include<iostream>using namespace std;class A{public: int _a; virtual void t() { cout << "A::t()" << endl; }};class B1 : public A{public: int _b1; virtual void b1() { cout << "B1::b1()" << endl; } void t() { cout << "B1::t()" << endl; }};class B2 : public A{public: int _b2; virtual void b2() { cout << "B2::b2()" << endl; } void t() { cout << "B2::t()" << endl; }};class D:public B1,public B2{public: int _d; void t() { cout << "D::t()" << endl; } void b1() { cout << "D::b1()" << endl; } void b2() { cout << "D::b2()" << endl; }};void test(){ D d; d.B1::_a = 1; d._b1 = 2; d.B2::_a = 3; d._b2 = 4; d._d = 5;}int main(){ test(); system("pause"); return 0;}
由结果可知,D类自己新加入的虚函数存放在了B1(派生类的继承列表中的第一个基类)的虚表中,再将在D类中对B1类和B2类的某些虚函数进行重写,所以就用D类重写的虚函数的地址覆盖了基类B1和B2中的某些虚函数。
2.菱形虚拟继承 (D中没有特有的虚函数,只是重写了B1和B2类的某些虚函数)
#include<iostream>using namespace std;class A{public: int _a; virtual void t() { cout << "A::t()" << endl; }};class B1 :virtual public A{public: int _b1; virtual void b1() { cout << "B1::b1()" << endl; } void t() { cout << "B1::t()" << endl; }};class B2 :virtual public A{public: int _b2; virtual void b2() { cout << "B2::b2()" << endl; } void t() { cout << "B2::t()" << endl; }};class D:public B1,public B2{public: int _d; void t() { cout << "D::t()" << endl; } void b1() { cout << "D::b1()" << endl; } void b2() { cout << "D::b2()" << endl; }};void test(){ D d; d._a = 1; d._b1 = 2; d._b2 = 3; d._d = 4;}int main(){ test(); system("pause"); return 0;}
由上图分析可知:将A类的虚表地址经过重写后只保存了一份,也就是说A类中的虚函数经过重写后成为所有类共有的, 而D将B1和B2的某些虚函数进行了重写。 可以看出,虚菱形继承增加了一个超类(B)的虚表地址。
易混淆概念解析
多态
多态可以简单的概括为”一个接口,多种方法”(接口重用),程序在运行时才决定调用的函数。C++的多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为重写,多态和非多态的实质区别在于函数地址的早还是晚绑定,如果编译期间就可以确定的,是早绑定也就是静态的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。
#include<iostream>using namespace std;class Base{public: virtual void Print() { cout << "Base::Print()" << endl; }};class Derived:public Base{public: virtual void Print() { cout << "Derived::Print()" << endl; }};void Print(Base* p){ p->Print();}void test(){ Base b; Derived d; Print(&b); Print(&d);}int main(){ test(); system("pause"); return 0;}
运行结果:
通过代码我们可以看出实现多态必须满足继承和在子类中重写父类的虚函数这两个条件,既然虚函数在多态中这么重要,那定义时有哪些需要注意的呢?
虚函数的定义遵循一下重要规则:
1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。
2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数,虚函数在类外实现时,在类内声明必须加关键字virtual,类外不能加
3.静态成员不能是虚函数,因为静态成员函数的特点是不受限制于某个对象
4.内联函数不能是虚函数,因为内联函数不能再运行中动态确定位置,即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看作是非内联的。
5.构造函数不能是虚函数,因为构造的时候吗,对象还没有初始化完成,只有构造完成后,对象才是具体的实例
6.析构函数通常声明为虚函数(编译器特殊处理,派生类和基类析构函数构成覆盖)
- 浅谈继承和多态
- 浅谈c++的精髓之继承和多态
- 浅谈封装继承多态
- 浅谈继承与多态
- 浅谈继承和组合
- 浅谈继承和组合
- 浅谈继承关系和接口
- C++浅谈组合和继承
- 浅谈继承和抽象类
- 浅谈 Java 和多重继承
- 浅谈利用多态,继承和接口来封装代码,提高Java代码的可扩展性
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 面向对象之继承和组合浅谈
- 欢聚时代2017校招笔试题目(JAVA基础类)A卷--3
- 顺序表应用3:元素位置互换之移位算法
- 深入理解计算机系统(1.1)------Hello World 是如何运行的
- Zookeeper的可靠性是怎么保证的?
- [NOI 2008] 志愿者招募
- 浅谈继承和多态
- 剑指offer(31)—整数1出现的次数
- hadoop scheduler.capacity queues 配置
- 解决给div中添加文字后位置发生偏移的问题
- 图片上传拦截器配置
- 图片操作异常 ImageIO: CGImageReadCreateDataWithMappedFile 'open' failed
- Oracle日期、字符串格式化函数,位数不足前面加0,一位数字显示两位,格式化数字为定长
- CentOs6.5虚拟机的创建
- 关于JavaScript函数的参数按值传递而不是按引用传递的分析