侯捷c++面向对象 笔记(四)

面向对象OOP

类与类之间的关系

  • 继承
  • 组合/复合
  • 委托

复合(COMPOSITION 表示has-a)

概念理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template< class T , class sequence = deque<T> >
class queue{
...
protected:
sequeue c;
public:
//以下完全利用对象 c 的操作函数来实现
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front(0) { return c.front(); }
reference back() { return c.back(); }
//deque(双端队列)是两端可进出,queue(队列FIFO先进先出)是末端进前端出
void push(const value_type &x ){ c.push_back(x); }
void pop(){ c.pop_front(); }
};

其中对于template< class T , class sequence = deque> 的意思为默认 sequence 的类型为 deque

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template< class T >
class queue{
...
protected:
deque<T> c; //底层容器
public://public部分不变
//以下完全利用对象 c 的操作函数来实现
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front(0) { return c.front(); }
reference back() { return c.back(); }
//deque(双端队列)是两端可进出,queue(队列FIFO先进先出)是末端进前端出
void push(const value_type &x ){ c.push_back(x); }
void pop(){ c.pop_front(); }
};

即queue里面的所有功能都没有自己写,他都是通过 C 来调用 deque 的成员函数来完成的,所有的功能都在 deque 中已经完成了,而 queue 是借用 deque 已经完成的功能来实现自己的功能。
对于上述的特例,进一步阐述 composition(复合) ,因为* deque* 的功能比较强大,而 queue 的功能都能通过借用 deque 来完成;这是 23种设计模式中的适配器模式 Adapter(适配,改造),简单的说即: A 拥有 B 则为 composition 。

生命周期上来看,内外部是同时的

内存分析:

套娃

组合/复合关系的构造函数和析构函数

  对于 composition(复合) 而言,其构造函数是由内而外的,比如说类 A 中拥有类 B ,则类A的构造函数首先调用内部的类 B 的 default(默认) 构造函数,之后才执行自己的构造函数;但是当类 B 含有多个构造函数时,此时编译器不知道该调用哪一个构造函数,则此时需要程序设计者在写 composition(复合) 的构造函数时写上具体调用类B的哪一个构造函数。   

委托(DELEGATION,COMPOSITION BY REFERENCE)

概念理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//handle 接口 指针指向为string实现所有功能的类StringRep
class stringRep;
class string{
public:
string();
string(const char *s);
string(const string &s);
string & operator=(const string &s1);
~string()
private:
StringRep *rep;
...
};

//body 实现 此段程序表示具体实现类
#include "String.hpp"
namespace{
class StringRep{
friend class string;
stringRep(const char *s);
~stringRep();
int count;
char *rep;
};
}

委托也可以看做是一种复合,但是他是使用指针相连
string内有StringRep指针指向,string随时可以委托任务给StringRep来做
生命周期上来看,他们是不一致的,不同步,string可能会先创建,StringRep在使用时创建

这种pImpl机制,在C++的pImpl——编译防火墙中有单独介绍

要注意一个点,a要改变hello,不能影响b和c,当要改变时,将使用写入时复制(英语:Copy-on-write,简称COW)机制,创建一个副本提供给a修改,过程如下图所示。更多可参考写入时复制(Copy-on-write)机制

例子中利用这一特性实现了引用计数,即实现了多个字符串对象共享同一内容(内存地址)的字符串

相关设计实践

组合模式

在计算机文件系统中,有文件夹的概念,文件夹里面既可以放入文件也可以放入文件夹,但是文件中却不能放入任何东西。文件夹和文件构成了一种递归结构和容器结构。
虽然文件夹和文件是不同的对象,但是他们都可以被放入到文件夹里,所以一定意义上,文件夹和文件又可以看作是同一种类型的对象,所以我们可以把文件夹和文件统称为目录条目,(directory entry).在这个视角下,文件和文件夹是同一种对象。
所以,我们可以将文件夹和文件都看作是目录的条目,将容器和内容作为同一种东西看待,可以方便我们递归的处理问题,在容器中既可以放入容器,又可以放入内容,然后在小容器中,又可以继续放入容器和内容,这样就构成了容器结构和递归结构。

原型模式

子类通过构造函数创建的对象添加到父类的数组(原型数组)中去,父类通过数组中的原型调用clone来得到原型(子类对象)的副本

继承(Inheritance 表示is-a)

C++三种继承方式

  • public:

  • private:

  • protected:

内存分析 & 继承关系的构造函数和析构函数

子类对象中有父类的成分
父类的析构函数必须是虚函数,否则会出现undefined behavior

继承与虚函数

概念

在任何一个成员函数之前加上virtual关键字,就是一个虚函数
在继承关系中,数据可以被继承下来,可以从内存的角度看。函数也可以,但不能从内存的角度看,函数的继承继承的是调用权,即:子类可以调用父类的函数——子类继承了父类,拥有父类函数的调用权
成员函数分为三类:

  • no-virtual函数,非虚函数:不希望子类/派生类重新定义(override,复写)它
  • virtual函数,虚函数:希望子类重新定义(override,复写)它,且父类已经有默认定义
  • pure virtual函数,纯虚函数:子类一定重新定义(override,复写)它,父类中没有默认定义(其实是可以有定义的)
文章作者: ゴウサク
文章链接: http://dapaner.top/2022/04/17/侯捷c-面向对象-笔记(四)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Corner&Coder
微信赞赏码
支付宝赞赏码