快捷搜索:  汽车  科技

c语言怎样实现继承(专题之继承与多态)

c语言怎样实现继承(专题之继承与多态)1.1类之间的关系继承是面向对象程序设计最主要的特征 可以说 没有掌握继承 就等于没有掌握类和对象的精华 就是没有掌握面向对象程序设计的真谛.正文黑色面向对象程序设计有4个主要特点 抽象 封装 继承 多态要更好地进行面向程序的设计 最重要的就是继承和多态 .本次主要给大家介绍继承和多态的相关知识.抽象和封装将在后续章节讲解.

写在前面:根据个人习惯将笔记进行颜色区分 为了方便大家阅读 做出简单说明

赠人玫瑰 手有余香.小羊伴你一路同行~~~~

补充说明部分 将会用绿色字体

重点将会用红色

正文黑色

面向对象程序设计有4个主要特点 抽象 封装 继承 多态

要更好地进行面向程序的设计 最重要的就是继承和多态 .本次主要给大家介绍继承和多态的相关知识.抽象和封装将在后续章节讲解.

继承是面向对象程序设计最主要的特征 可以说 没有掌握继承 就等于没有掌握类和对象的精华 就是没有掌握面向对象程序设计的真谛.

1.1类之间的关系

包含关系has-A:一个类中的数据成员已经是另外一个定义的类.一个类的属性由另外一个类组成

使用关系uses-A:一个类部分的使用另一个类.通过类之间成员函数的相互联系 定义友员或对象参数传递实现.

is-A 机制称为"继承".关系具有传递性 但不具有对称性.(这句话的意思就在说 c 程序员属于程序员 但是不是所有的程序员都是c 程序员)

继承是类之间定义的一种重要关系 一个B类继承A类 或称从A类派生B类 称A类为基类(父类) B类为派生类(子类)

c语言怎样实现继承(专题之继承与多态)(1)

继承关系简单图示

万事万物当中 都有继承.例子有很多 这里不再赘述.

1.2继承的语法

class 子类名 : 继承方式1 基类(父类)1 继承方式2 基类2 ... {

...

};

继承方式:

公有继承 - public - 最常用方式

私有继承 - private - 缺省方式

保护继承 - protected - 特殊的私有继承

公有继承

1.通过继承,在基类中定义的任何成员,也都成为了子类的成员,但是基类的私有成员,子类虽然拥有却不能直接访问。(可以比如是爸爸的配偶 子类不能继承父类的配偶)

2.基类中的保护成员,可以被子类直接访问,但不能在无关的类和全局域中被访问。(爸爸的银行密码)

3.任何一个子类对象中都包含着它的基类子对象。如果在子类的构造函数中没有明确指明其基类子对象如何被构造,系统将采用无参的方式构造该子对象。如果在初始化表中指明了基类子对象的构造方式,就调用相应的构造函数构造该子对象。

4.子类对象的构造和析构顺序

按照继承表的顺序依次构造每个基类子对象->按照声明的顺序依次构造每个成员变量->执行子类构造函数体中的代码

析构的过程与构造严格相反

5.一个子类对象在任何都可以被视为它的基类对象——IsA。

任何时候,一个子类对象的指针或者引用,都可以被隐式地转换为它的基类类型的指针或者引用,但是反过来,将基类类型的指针或者引用转换为它的子类类型必须显示地(static_cast)完成。

Student s (...);

Human* h = &s; // OK !

6.在子类中定义的任何和基类成员同名的标识符,都可以将基类中的该成员隐藏起来。通过作用域限定操作符“::”,可对该成员解隐藏。

私有继承和保护继承

用于防止或者限制基类中的公有接口被从子类中扩散。

class DCT { public: void codec (void) { ... } }; class Jpeg : protected DCT { public: void render (void) { codec (...); } }; Jpeg jpeg; jpeg.codec (...); // ERROR ! //Jpeg Has A DCT,实现继承 class Jpeg2000 : public Jpeg { public: void render (void) { codec (...); // OK ! } }; 八、多重继承

从多于一个基类中派生子类。

电话 媒体播放器 计算机

1.多重继承的语法和语义与单继承并没有本质的区别,只是子类对象中包含了更多的基类子对象。它们在内存中按照继承表的先后顺序从低地址到高地址依次排列。

2.子类对象的指针可以被隐式地转换为任何一个基类类型的指针。无论是隐式转换,还是静态转换,编译器都能保证特定类型的基类指针指向相应类型基类子对象。但是重解释类型转换,无法保证这一点。

3.尽量防止名字冲突。

继承方式对访控属性的影响

子类继承了父类的全部成员变量和成员方法(除了构造和析构之外的成员方法) 但是这些成员的访问属性 在派生的过程中是可以调整的

c 中继承方式对子类访问属性的影响

1)public继承:父类成员在子类中保持原有访问级别.

private继承;父类成员在子类中会变成private成员.

protected继承:父类中public成员会变成protected

父类中protected成员会变成protected

父类中private成员仍然为private

2)private成员在子类中依然存在 但是却无法访问到 不论哪种方式继承 派生类都不能直接使用基类的私有成员.

3)"三看"原则

1看调用语句 这句话写在子类的内部 还是外部

2看子类如何从父类中继承

3看父类中的访问级别

继承中的构造和析构

直接来一个例子吧

#include <iostream> using namespace std; class Human { public: Human (const string& name int age) : m_name (name) m_age (age) {} //构造函数 void who (void) const { cout << m_name << "," << m_age << endl; } void eat (const string& food) const { cout << "我在吃" << food << endl; } protected: string m_name; int m_age; }; class Student : public Human { public: Student (const string& name int age int no) : Human (name age) m_no (no) {} //正确的构造函数创建Student类的对象的同时先创建基类Huamn类,所以会调用Human类的构造函数,这里指定Human所调用的构造函数与所写的构造函数相匹配,所以不会出错。 /* Student (const string& name int age int no) : m_no (no) { m_name=name; m_age=age; } */ 错误的构造函数会报错,原因是这里调用Human的构造函数时没有指定方式,默认使用无参构造,但是在基类Human类中没有无参构造,所以会报错 Student (const Student& that) : Human (that) m_no (that.m_no) {} //拷贝构造,显式的指明了调用基类的拷贝构造函数 Student& operator= (const Student& that) { //操作符重载 if (&that != this) { Human::operator= (that);//显式的调用基类的拷贝赋值 m_no = that.m_no; } return *this; } void learn (const string& lesson) const { cout << "我(" << m_name << "," << m_age << "," << m_no << ")在学" << lesson << endl; } using Human::eat;//如果没有这句,则Human的eat与这里的eat作用域不再一起,则下面的eat不会与Human中的eat构成重载,而是构成隐藏关系 //但是有了这句之后,将Human的eat在这里可见,既作用域也被声明在这里,则两个eat构成了重载关系 void eat (void) const { cout << "我绝食!" << endl; } // int eat; private: int m_no; }; int main (void) { Student s1 ("张飞" 25 1001); s1.who (); s1.eat ("包子"); s1.learn ("C "); Human* h1 = &s1;//子类的指针可以隐式转换为基类的指针,因为访问范围缩小了,是安全的 h1 -> who (); h1 -> eat ("KFC"); // h1 -> learn ("C");//基类的指针或对象不可以访问子类中的成员 Student* ps = static_cast<Student*> (h1);//基类的指针不可以隐式的转换为子类的指针,因为访问范围扩大,不安全,所以必须显式的进行转换,但是这样有风险 ps -> learn ("C"); Student s2 = s1; s2.who (); //子类的指针或对象可以方位基类的成员 s2.learn ("英语"); Student s3 ("赵云" 20 1002); s3 = s2; s3.who (); s3.learn ("数学"); return 0; }

虚函数与多态

如果将基类中的一个成员函数声明为虚函数,那么子类中的同型函数就也成为虚函数,并且对基类版本形成覆盖。这时,通过一个指向子类对象的基类指针,或者一个引用子类对象的基类引用,调用该虚函数时,实际被调用的函数不由该指针或引用的类型决定,而由它们的目标对象决定,最终导致子类中覆盖版本被执行。这种现象称为多态。

虚函数

1 通过虚函数表指针VPRT调用重写函数是在程序上运行的 因此需要通过寻址操作上才能确定真正应该调用的函数 而普通成员的函数是在编译的时候就确定了调用的函数 在效率上 虚函数的效率会低很多

2 这就是说 我们可以把所有成员函数声明成虚函数 但是没有必要 因为执行效率太低.

3 c 编译器 执行howtoprint函数 不需要区分是子类对象还是父类对象

1什么是多态?

调用同样的语句 有多种不同的表现形式.

2多态实现的三个条件

有继承

有virtual重写

有父类指针(引用)指向子类对象

3多态的c 实现

virtual关键字告诉编译器 这个函数要支持多态 不是根据指针类型判断如何调用 而是要根据指针所指向的实际对象类型来判断如何调用.

4多态的理论基础

动态联编 静态联编.根据实际情况来判断重写函数的调用

5多态的意义

设计模式的基础

6实现多态的理论基础

函数指针做函数参数

下面就来一个多态的例子吧

#include"iostream" using namespace std; class Liberation { public: virtual int Power() { return 70; } }; class Armed :public Liberation { public: int Power() { return 90; } }; class Pleice :public Liberation { public: int Power() { return 170; } }; class Thief { public: int Attack() { return 112; } }; void objPlay(Liberation &sun Thief &di) { if (sun.Power() < di.Attack()) { cout << "小偷好厉害呀 好怕怕" << endl; } else { cout << "警察胜利啦 啦啦啦" << endl; } } void main() { Liberation lib; Armed arm; Pleice ple; Thief thi; objPlay(lib thi); objPlay(arm thi); objPlay(ple thi); system("pause"); return; }

纯虚函数、抽象类、纯抽象类

形如:

virtrual 返回类型 成员函数名 (形参表) = 0;

的虚函数被称为纯虚函数。

一个包含了纯虚函数类称为抽象类,抽象类不能实例化为对象。

如果一个类继承自抽象类,但是并没有为其抽象基类中的全部纯虚函数提供覆盖,那么该子类就也是一个抽象类。

class A { // 纯抽象类

virtual void foo (void) = 0;

virtual void bar (void) = 0;

virtual void fun (void) = 0;

};

class B : public A { // 抽象类

void foo (void) { ... }

};

class C : public B { // 抽象类

void bar (void) { ... }

};

class D : public C { // 具体类

void fun (void) { ... }

};

除了构造和析构函数以外,所有的成员函数都是纯虚函数的类称为纯抽象类.

当然关系继承和多态的知识 远远不止于此.以后会继续补充的 感谢观看.

猜您喜欢: