Boolan C++(上)3

1.组合与继承

1.1组合

1.1.1设计模式 Adapter-改造

现在我们设计了一个 功能很强大的类,但是我们考虑到由于版本发布的问题(精简版,企业版,旗舰版。。。),我们不可能发布一个版本就去开发一个,这样成本实在太高了,仔细思考以下,既然有了一个很强大的旗舰版,那么其他削弱的版本从旗舰版删删减减不就好了?

这种面向对象的设计模式就是改造

标准库里头有一个例子:queue & deque


deque 是两头都能出,queue是先进先出(只有一个出口)voidpush(constvalue_type&x){c.push_back(x);}voidpop(){c.pop_front();}};

deque 是两头都能出,queue是先进先出,deque的功能明显比queue强大的多,所以queue完全可以由deque实现。

当然改造不只限于删删减减,也可以再类里加上其他的类对象,在现实生活中我们的汽车都是万国牌的,这就好比在汽车这个类是中有许许多多的其他东西(座椅类,引擎类,安全气囊类。。。),我们在写类的时候也可以效仿。

在C中,在struct包涵其他的struct就相当于改造。

5.1.2复合下的构造和析构如何调用?

构造函数由内而外调用:


容器的构造函数首先调用组成部分的默认构造函数,然后执行自己的,如果想指定调用组成的构造函数如下

Container::Container(...):Component(){...};

析构函数由外而内调用:

容器先调用自己的析构函数,在调用组成的析构函数,如果想指定调用如下

Container::~Container(...){...~Component()};

1.2.1 设计模式 Delegation-委托

客户端+服务器的架构叫C/S架构,客户端把信息收集起来发送到服务器,由服务器统一处理,这样在功能升级的时候,只需要在服务器端的有较大的改动,而客户端几乎不变,例如:有一个客户端提供银行账号登录,后来由于公司业务拓展,现在可以支持QQ账号登录,这种改动对应客户端只是用户改变输入的账号与密码,主要的改动只在服务器上进行。

为了满足这种设计需求,就可以使用 设计模式 委托,在类中使用一个指针指向别的功能类,自身的功能完全借由别的类来完成(可以随时改变指向),保持自身面向客户不变。这种手法也叫编译防火墙。


现在有a,b,c三个String类的对象,同时共享一个StringRep对象:


这个时候如果a想要更改内容,那么就必须拷贝一份副本给a改,不能影响到b和c。

5.2继承

Dog属于动物类,从动物类继承:


继承就是一种传承,这里涉及到两个类,一个叫父类(基类)另一个叫子类(派生类),子类可以传承父类的所有的数据,还可以对父类进行扩展。

继承的作用在于代码复用与扩展,继承后的子类一定是大于等于父类的。

5.2.1继承下的构造与析构调用


继承中构造 和 析构的调用顺序:

构建子类对象一定会先调用父类的构造函数,子类默认调用父类的无参构造,当然也可以指定调用父类的构造函数,需要通过初始化列表指定调用

Derived::Derived(...):Base(){...}

析构函数的调用顺序 和 构造函数调用顺序相反

Derived::~Derived(...){...~Base()}

注意:

base class 的析构必须是virtual,否则可能会出现undefined behaviour

1.2.2c++中的继承方式

(1)公开继承 class Child:public Parent{};

公开继承下 父类数据到子类之后的权限变化

父类的公开数据 到子类之后是公开的

父类的保护数据 到子类之后是保护的

父类的私有数据 到子类之后是隐藏的

(2)保护继承 class Child:protected Parent{};

保护继承下 父类数据 到子类之后的权限变换

父类的公开数据 到子类之后是保护的

父类的保护数据 到子类之后是保护的

父类的私有数据 到子类之后是隐藏的

(3)私有继承 class Child:private Parent{};

私有继承下 父类数据 到子类之后的权限变换

父类的公开数据 到子类之后是私有的

父类的保护数据 到子类之后是私有的

父类的私有数据 到子类之后是隐藏的

总结:所谓的继承方式,就是父类能给子类的最高权限,实际权限小于等于这个权限,父类的私有数据 到子类之后一定是隐藏的。

class A: B 没写权限就是默认私有的

2.虚函数


Shape是一个很抽象的概念,下面的长方形,椭圆是一个具体的概念,考虑到两个图形都有是一种形状,为了增加代码复用,抽象出Shape这个类用来描述我们希望图形对象所具有的共性。

纯虚函数draw在基类中,这样就迫使子类里头必需出现纯虚函数的定义,确保子类图形能够被绘制。

虚函数error,由于不同的图形可能在操作中会出现错误,针对不同的对象需要重新定义错误处理,当子类出错那么会调用子类对应的error,基类的error提供了一个通用的错误处理,如果子类没有重新定义error,那么就延续父类的处理方式。

成员函数objectID,我们希望给创建的所有对象编号,这不需要子类去重新定义编号方式,所以使用成员函数。

2.1运行时绑定

virtual让类具有多态性,有的功能函数不是父类可以实现的 或者 父类只能提供一个泛用的方法,那么使用virtual让方法在子类中实现,这样在调用子类时不会受到父类的干扰。

在软件开发中那些泛用的,谁写都一样的东西叫框架,框架大量的使用了virtual,最牛逼的就是MFC(虽然这东西现在名声不咋滴)。

在C++中支持相关对象的不同的成员函数(原型相同),并允许对象与适当的成员函数进行运行时的绑定,C++通过重写(overwrite)支持这种机制----多态

在上面代码中我们希望从键盘输入一个非零的整数让程序输出不同的消息,但是上述代码无论输入什么,打印的都是Animal。这是因为指针p的指向的say函数是编译的时候就确定的。

接着在基类函数前面加上virtual

现在编译后,输出的结果就和我们从键盘输入的内容有关了。

多态这种性质只发生在父类型的指针(引用) 指向子类对象时,通过父类型的指针调用

虚函数,如果子类重写了这个虚函数 ,则调用的表现是子类的,否则就是父类型中对应的实现。继承是构成多态的基础,虚函数是构成多态的关键 ,函数重写是必备条件

2.1.1 重载,覆盖,隐藏的区别

(1)重载:

在同一个类中

函数名字相同,参数不同

virtual关键字可有可无

(2)覆盖:

在不同的类中(基类与派生类)

函数名字相同,参数相同

基类必需有virtual关键字

(3)隐藏

在派生类中函数与基类中的函数同名,参数不同,无论有没有virtual,基类函数都会被隐藏

在派生类中函数与基类中的函数同名,参数相同,但是基类没有virtual,此时发生隐藏

2.1.2多态的应用 (类型更加通用 根据具体的对象做出具体行为)

用在函数的参数上

void testAnimal(Animal* a);

用在函数的返回值上

Animal* getAnimal(int s);

2.1.3类型识别

因为多态让子类对象富有个性化,也做到了类型通用,但是通用就意味着失去个性化,那么在我们设计程序时可以通过类型识别恢复子类的个性。

2.1.3.1 typeid

typeid这个运算符可以获得类型或者对象的类型信息,使用时需要包涵#include 。

typeid 返回的信息存入一个type_info 类型的对象中,这个类型 重载 == 和 !=运算符。并且有个成员函数 name()返回类型的名称。

如果父子类之间 没有多态性,则当父类对象指针指向子类对象时,通过指针取值识别出的对象是父类型的。


运行后输出结果如下:


i指的是int,Pi指的是int*,P6Animal指的是Animal*,6Animal指的是Animal。


2.2 虚析构

有以下继承:


我们通过两种不同的方式创建对象

(1)


没有任何问题。

(2)


少了调用了一次B的析构函数,这样就造成了内存泄露。

解决方法:

使用虚析构后,资源被释放。

2.2.1虚表

virtual之所以能呈现出多态的特性,这是因为在底层使用了虚函数表,通过指针连接不同的代码块。

如果基类的析构函数没有被声明成virtual函数,那么在虚表中就不会出现析构函数,那么class B中的析构函数会将class A中的析构函数隐藏,然而指针是A*类型,在调用析构的时候只会调用基类的析构,这样就导致了内存泄露,加上virtual之后,delete时会先去调用子类的析构函数,当子类的析构被调用,父类的析构会自动被调用,这样就避免了内存泄露。


2.2.2使用指针直接操作虚表

指针是一个大流氓,得到了地址一切的权限都只是君子协议


3.Delegation(委托)+Inheritance(继承)

设计一个Subject类,其中使用向量容器来保存观察窗口,同时需要更新数据时直接调用set_val函数。

在写一个基类Observer

子类PPT(举例而已)

主函数

编译运行后,当我们改变了其中一个的数据,其余的4个对象跟着一起改了,这样就可以实现窗口联动或者一些需要关联的操作,这叫面向对象设计。

注:需要#include

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 1.组合与继承 1.1组合 1.1.1设计模式 Adapter-改造 现在我们设计了一个 功能很强大的类,但是我们...
    长江小杨阅读 271评论 0 0
  • 收集非原创文章,如遇原作者,请私聊我,我会表明出处! 1--10 1. C++中什么数据分配在栈或堆,静态存储区以...
    Juinjonn阅读 4,933评论 0 30
  • 1.面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。 2.什么是类?...
    少帅yangjie阅读 4,983评论 0 14
  • 继承和多态 1. 继承的优缺点 优点:(1)子类可以灵活地改变父类中的已有方法;(2)能够最大限度的实现代码重用。...
    MinoyJet阅读 614评论 0 0
  • 感情🐻中保持理智,对自己有利,对自己好,跟父母沟通,交流。 跟朋友交流 满足自我
    summerrain阅读 83评论 0 0