【GeekBand】C++面向对象高级编程-第三周笔记

课堂大纲:

1.组合与继承
1.1 Composition 复合
1.2 Delegation 委托
1.3 Inheritance 继承
2.虚函数与多态
2.1虚函数


正文

1.组合与继承

1.1Composition 复合

复合表示has-a,表示一个类里含有另一个类的对象(非指针及引用)。
例如

    template<class T>
    class queue
    {
        ...
    protected:
        deque<T> c;   //底层容器
    };

其中c是该类的一个成员,是c这个对象及其成员变量是占据queue内存的。
UML表示方法是:

2016-05-26_082626.png

黑色实心菱形从queue类指向deque类,一个简单的记忆方法是:菱形是实心的,表示这个queue是真的有这个deque的实体,然后这个菱形指向了deque类,表示这个菱形表示deque。
Composition关系下的构造和析构
构造:
构造是由内而外,就像打包裹,从里往外。先构造components的对象再构造container,这是编译器自动完成的。

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

析构:
析构是由外而内,就像拆包裹,从外往里。先析构Container再析构components的,这也是编译器自动完成的。

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

1.2 Delegation委托

委托表示composition by reference,表示一个类里含有另一个类的指针或者引用对象。
例如:

class String
{
private:
    StringRep* rep; //pimml
};

string.JPG

黑色空心菱形从String类指向StringRep类,一个简单的记忆方法是:菱形是空心的,表示这个String是只有这个StringRep的指针对象或者引用,然后这个菱形指向了StringRep类,表示这个菱形表示StringRep类。
著名的写法:编译防火墙
一个类A只提供接口,具体实现用另一个类B来完成,其中A与B的 关系是委托关系,好处是可以切换具体实现,不影响接口。
编译防火墙.JPG

a b c共享数据,如果a要改数据,那么系统就会复制一份专门给a修改,而不会影响b和c 的使用。

1.3Inheritance 继承

复合表示is-a
例如:

struct _List_node_base
{
    _List_node_base* _M_next;
    _List_node_base* _M_prev;
};
template<typename _Tp>
struct _List_node: public _List_node_base
{
    _Tp _M_data;
};

UML表示方式为:

继承.JPG

图中表示的方式与前面两种略有不同,结合三角形,可以看成是有子类指向父类,这是一个泛化的过程,譬如老虎→动物。
其中继承方式有public、protected、private三种,现简单介绍一下其特性。

public protected private
公共继承 public protected 不可见
私有继承 private private 不可见
保护继承 protected protected 不可见

在上图中:1)基类成员对派生类都是:共有和保护的成员是可见的,私有的的成员是不可见的。
** 2)基类成员对派生类的对象来说:要看基类的成员在派生类中变成了什么类型的成员。如:私有继承时,基类的共有成员和私有成员都变成了派生类中的私有成员,因此对于派生类中的对象来说基类的共有成员和私有成员就是不可见的。**

父类中的数据会被子类继承下来,但是子类能否直接访问这些数据需要根据上表的特性来考虑。
当继承与虚函数搭配时,能充分发挥出继承的价值。关于虚函数,下文会提到。
继承关系下的构造和析构
构造:
继承关系下的构造还是由内而外地,derived类先构造base类的,再构造自己的,这是编译器自动完成的

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

析构:
析构则是由外而内地,先析构derived的再析构base的,这也是编译器自动完成

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

值得注意的是,《Effective C++》条款07中有提到

当derived class 对象经由一个base class指针被删除,而该base class 带有一个non-virtual 析构函数时,其结果未有定义——实际执行时通常发生的是对象的derived成分没被销毁。
消除这个问题的做法很简单:给base class 一个virtual析构函数。此后删除derive class对象就会如你想要的那般。

读者可以简单地写一个类来测试一下,这里笔者就随便写一个例子来说明这个问题。

#include<iostream>
using namespace std;

class Base
{
public:
    Base( ) { cout << "I'm Base's Ctor" << endl; }
    ~Base( ) { cout << "I'm Base's Dtor" << endl;}
};

class Derived: public Base
{
public:
    Derived( ) { cout << " I'm Derived's Ctor"<<endl; }
    ~Derived( ){cout<<"I'm Derived's Dtor"<<endl;}
};

int main()
{
    Base *base = new Derived;
    delete base;
}

运行结果:


捕获.JPG

可以清楚看到这就发生了上述内存泄露的问题了。为此
修改程序如下:

#include<iostream>
using namespace std;

class Base
{
public:
    Base( ) { cout << "I'm Base's Ctor" << endl; }
    virtual ~Base( ) { cout << "I'm Base's Dtor" << endl;}
};

class Derived: public Base
{
public:
    Derived( ) { cout << " I'm Derived's Ctor"<<endl; }
    ~Derived( ){cout<<"I'm Derived's Dtor"<<endl;}
};

int main()
{
    Base *base = new Derived;
    delete base;
}

运行结果如下:

捕获1.JPG

所以在父类的析构函数中加了virtual关键字后,delete父类指针时可以还调用子类的析构函数,从而避免了内存泄露。
值得提醒一下的时,《Effective C++》07条款中给的提醒是:

无端地将所有classes的析构函数声明为virtual,就像从未声明它们virtual一样,都是错误的。许多人的心得是:只有当class内含至少一个virtual函数才为它声明virtual析构函数

好了,终于进入下一部分了。

2.虚函数与多态

2.1虚函数

首先简单介绍三个关于虚函数的名词:
non-virtual函数:非虚函数,这个函数是你不希望子类重新定义它。
virtual函数:虚函数,你希望子类重新定义,而且父类中已经定义过这个函数。
pure virtual函数: 纯虚函数,你希望子类一定要重新定义且父类中并无默认定义。
例子如下:

class Shape
{
public:
    virtual void draw( ) const = 0; //纯虚函数
    virtual void error( const std::string& msg);//虚函数
    int objectID( ) const;//非虚函数
    ...
};

class Rectangle: public Shape {...};
class Ellipse:public Shape{...};
```
**继承+复合关系下的构造和析构**

![捕获3.JPG](http://upload-images.jianshu.io/upload_images/2020078-f84f329c06b6ff91.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
三个类的关系如上图所示,那么我们用简单的程序测试一下:

```C++
#include<iostream>
using namespace std;
 
class Base
{
public:
  Base(){cout<<"Base ctor!"<<endl;}  
  ~Base(){cout<<"Base dtor!"<<endl;}
  int base;
};
 
class Component
{
public:
  Component(){cout<<"Component ctor!"<<endl;}  
  ~Component(){cout<<"Component dtor!"<<endl;}
  int component;
};
 
class Derived:public Base
{
public:
  Derived(){cout<<"Derived ctor!"<<endl;} 
  ~Derived(){cout<<"Derived dtor!"<<endl;}
  int derived;
  Component cpt;
};
 
int main()
{
  Derived d;
  return 0;
}
```
运行结果是:
```
Base ctor!
Component ctor!
Derived ctor!
Derived dtor!
Component dtor!
Base dtor!
```
那么如下图的关系呢,我们再修改一下程序测试一下:
![捕获4.JPG](http://upload-images.jianshu.io/upload_images/2020078-641806665254d409.JPG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
```C++
#include<iostream>
using namespace std;
 
 
class Component
{
public:
  Component(){cout<<"Component ctor!"<<endl;}  
  ~Component(){cout<<"Component dtor!"<<endl;}
  int component;
};
 
class Base
{
public:
  Base(){cout<<"Base ctor!"<<endl;}  
  ~Base(){cout<<"Base dtor!"<<endl;}
  int base;
  Component cpt;
};
 
class Derived:public Base
{
public:
  Derived(){cout<<"Derived ctor!"<<endl;} 
  ~Derived(){cout<<"Derived dtor!"<<endl;}
  int derived;
};
 
int main()
{
  Derived d;
  return 0;
}
```
运行结果是:
```C++
Component ctor!
Base ctor!
Derived ctor!
Derived dtor!
Base dtor!
Component dtor!
```
和打包裹的例子是一致的,打包(构造)的时候是先包装最里面的,然后再一层一层装外面的。拆包(析构)都是从最外面的包装开始拆起,直到拆到后面发现盒子里只剩下这么一点空气了:)

**委托+继承**
*待续...*

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

推荐阅读更多精彩内容