C++高级编程 第十章: 探索继承技术

来源:互联网 发布:pdf.js 打开 word文档 编辑:程序博客网 时间:2024/06/16 00:15

其实这一章在C++ primer讲得比较多,例如 基类和派生类之间得关系, 如何使用继承实现多态性…… 我觉得这一章C++ primer讲得比较好.

1. 使用继承构建类

我们要知道: 自定义得类a, 当继承了一个父类b得时候, a会自动包含了父类b的成员和成员函数. !!!

那么那些关键字到底有毛用?
1.子类: public 继承:

  • 父类:public 出来的是: public
  • 父类:protected 出来的是: protedted
  • 父类:private 出来的是: private

派生类的成员函数,可以访问基类的public成员、protected成员,但是无法访问基类的private成员。
派生类的实例变量,可以访问基类的public成员,但是无法访问protected、private成员

2.子类: protected继承

  • 父类:public 出来的是: protected
  • 父类:protected 出来的是: protected
  • 父类:private 出来的是: private

派生类的成员函数,可以访问基类的public成员、protected成员,但是无法访问基类的private成员.
派生类的实例变量,无法访问基类的任何成员,因为基类的public成员在派生类中变成了protected.

3.子类: private继承

  • 父类:public 出来的是: protected
  • 父类:protected 出来的是: protected
  • 父类:private 出来的是: private

派生类的成员函数,可以访问基类的public成员、protected成员,但是无法访问基类的private成员。
派生类的实例变量,无法访问基类的任何成员,因为基类的所有成员在派生类中变成了private。

……………………………………………………………………………………………………………………………………….
我们要知道: 当一个父类的某个成员函数使用了 virtual 关键字, 表明这个函数是虚函数, 并且表明其子类应当重载此虚函数.
当定义一个父类a对象,调用虚函数,会执行父类的虚函数,
当定义一个派生来b对象, 调用虚函数,会执行派生类里面的虚函数版本.
!!!在书里面:明确提到 应该将所有方法设置为vurtual.!!!

当我们使用指针的时候:
可以创建一个父类的指针指向一个派生类的对象: A *a = new B();
不可以用一个派生类的指针指向一个父类的对象: B *b = new A();
因为父类有的东西, 派生类一定有, 而派生类有的东西, 父类不一定有, 所以当创建派生类的指针指向父类, 有一部分是指向未知领域, 因为这部分派生类有而父类没有.

当我们创建一个父类指针指向一个派生类对象的时候:
如果我们的父类的成员函数a(); 设置成了虚函数.并且派生类重载了这个a();
那么我们在使用这个指针调用a()时候, 会调用派生类的a(),而不是父类的a();
一句话总结就是, 当父类指针或者引用在引用派生类的时候, 派生类会保留virtual重载的方法. 但是在强制转换的时候: 就是子类强制转换为父类对象时, 子类会丧失”保留virtual重载的方法”这个特性. 这个成为”切割”

2. 构造函数和析构函数的顺序

一个类的对象(包括父类和派生类) 他的构造顺序是:
如果有的话, 先构造父类
非static数据成员按照声明的顺序构造
执行构造函数体

class some{public:    some(){cout<<"2";}}class a{public:    a(){cout<<"1";}}class b :public a{public:    b(){cout<<"3"}protected:    some sss;}int main(){    b test;}

执行以上代码后 会输出“123”

一个类的对象(包括父类和派生类) 他的析构顺序是:
调用析构函数体
按照构造时候声明的反序去删除成员
如果有父类,就调用父类的析构函数.

输出“123”这些就自己上机实验把

在这里特别提醒: 我们应该为每个析构函数都设置成虚函数, 就是表明它的派生类都要写自己一个析构函数. 目的就是为了防止: 当我们存在一个父类指针指向一个派生类的对象, 并且派生类的析构函数没有标明virtual. 此时开始析构, 由于派生类没有自己的析构函数, 虚函数是动态绑定的基础。现在析构函数不是virtual的,因此不会发生动态绑定,而是静态绑定,指针的静态类型为基类指针。那么delete的时候就会跳过派生类的析构而直接执行父类的析构函数,从而造成内存泄漏. !!!

3.充分利用多态

看了一天关于这个东西, 大概对有一个粗略的了解.
什么是充分利用多态, 就是利用C++的语言特性去更好的表达生活的场景. 例如抽象类等等. 我暂时就了解这么多, 说的不好就评论抨击我把

首先我们先了解: 纯虚函数

class a{public:    a();    ~a();    void setNum() = 0;    void getNum() = 0;    void aver()=0;}class b:public a{public:    b();    ~b();    virtual void setNum(){xxxxx};    virtual void getNum(){xxxxx};    virtual void area(){xxxxx};}

怎么理解纯虚函数呢, 就是在类里面定义的时候后面加一个”=0”相当于关键字把. 然后这个函数的内容是不可以定义任何东西的, 而且一旦某一个类一旦有一个纯虚函数, 那么这个类就叫做纯虚类, 我们帮它当作接口类来理解,!!!
就是描述这个接口有什么函数, 这个接口具体是干什么的
然后通过这个接口类, 我们需要具体实现的细节通过这个接口类的派生类去完成.

接下来,我们再来看: 到底创建一个指向派生类对象的父类指针到底有什么用?

网上很多人都说: 创建一个指向派生类对象的父类指针 可以更好地用父类的对象去控制子类.
一开始不明白……..直到看到这个例子
做一个画图程序的时候,你可以画方,画圆,画线
按照OOP的思想方,圆,线都属于Shape类型
那么应该有
class Shape{}
class Fang extends Shape{}
class Yuan extends Shape{}
class Xian extends Shape{}

然后把他们画出来的操作方法在某个类里,这个方法接收一个图形,我不管你是什么实际的形状,你只要给我一个图形,我就可以把它画出来,看这个方法的定义
public void draw();那里面的参数应该怎么给,参数定义Fang f还是Yuan y或是Xian x,不管你定义成什么都只能接收一个实际的形状,如果你的参数定义成Shape s,那么就可以接受继承Shape的所有实际形状
public void draw(Shape s);
看出父类引用子类的好处了吗

也就是我们可以直接通过父类对象去创建我们想要的东西, 而不是直接去new一个yuan, new一个xian , 如果选择后者, 那我们要继承来干嘛呢对吧?

然后无意中还了解到了这个东西, 虽然是茫茫大海里面的一滴水

在C++高级编程书里面, 会看到一句话, “建议我们对父类的所有构造函数都set上virtual的关键字” 为什么要这样呢?

原因就是
当我们创建一个指向派生类对象的父类指针时, 我们用这个对象去调用成员函数.
1. 只有我们设置函数成虚函数, 我们才可以通过这个对象去访问派生类的函数
2. 如果没有设置, 就智能访问父类的函数, 父类的成员, 因为这个对象终究是一个父类对象, 取决定性作用的是这个指针是什么对象(类型), 而不是这个指针指向什么值.

#include<iostream>using namespace std;class father {public:    father();    //father();    virtual void print();    void notvirprint();    int nFather;};class son :public father {public:    son();    //virtual~son();    virtual void print();    void notvirprint();    int nSon;};int main() {    father *a = new son();    a->print();    a->notvirprint();    system("pause");    return 1;}father::father() {    cout << "父类构造函数运行" << endl;}void father::print() {    cout << "父类的print函数" << endl;}void father::notvirprint() {    cout << "父类的notvirtual函数" << endl;}son::son() {    cout << "子类构造函数运行" << endl;}void son::print() {    cout << "子类的print函数" << endl;}void son::notvirprint(3 {    cout << "子类的notvirtual函数" << endl;}

会输出:
父类构造函数在执行
子类构造函数在执行
子类的print函数
父类的notvirtual函数

利用这个virtual特性, 我们可以更好地去实现多态

4.关于static方法的覆盖

在C++里面, 是不可以覆盖static方法的,如果派生类存在一个a的static函数,父类也存在一个a的static函数,那么这两个是独立的函数, 因为static方法属于自己的类.
如果也出现了所谓的父类的指针/引用指向一个派生类的对象, 就记住这句话:取决定性作用的是这个指针是什么对象(类型), 而不是这个指针指向什么值.

原创粉丝点击