C++学习笔记2-类之间的关系
这一部分主要介绍C++面向对象,多个类之间的关系。另外介绍了虚函数及其实现机制和一些设计模式。
- Inheritance 继承 is-a
- Composition 复合 has-a
- Delegation 委托
Composition 复合 has-a
class queue
{
...
protected:
deque<T> c;
public:
void pop() {c.pop_front();}
};
类queue是一个队列,deque是双向队列,比queue功能强大。这里设计queue包含deque,所以是has-a,这种设计模式又叫做adapter.
构造函数
构造由内而外:Container::Container(): Component(){…}; //默认调用默认构造函数
析构由外而内:Container::Container(){… ~Component()};
Delegation-composion by reference
String
{
StringRep* rep;
}
一个类有另一个类的指针。复合的生命是同步的,有了A就有B,但是委托的生命不是同步的,当需要调用b的时候b才会生成。这种写法叫做pimpl(pointer to implementation或者handle/body)。这种方法的好处是,String类的具体实现可以不同,但是接口都一样,所以客户端不变,实现方法可以变化。
上面这个例子的图示如上,每生成一个String类,都会增加一个reference counting,共享空间。如果a要改变数据,会单独拷贝一份数据给a。
Inheritance 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;
}
构造由内而外,析构由外而内
**base class 的dtor必须是virtual,否则会出现undefined behavior **
inheritance with virtual function
- non-virtual函数 :不希望derived class重新定义它
- virtual函数:希望derived class 重新定义它(override),并且对它已经有默认定义。
- pure virtual:希望derived class一定要重写它,并且没有默认定义。
举例:
class Shape{
public:
virtual void draw() const=0; //pure virtual
virtual void error(const std::string& msg); //impure virtual
int objectID() const; //non-virtual
}
应用:Template Method(先搭出来一套框架,具体实现延迟到应用再实现)
如上图,对于打开文件这个动作,可以先设计一套框架,但是对于serialize这个函数,父类无法实现,于是声明为虚函数。当调用的时候,子类的指针调用父类的函数,但是具体实现是用的子类复写的方法。过程见灰色的线。
Inheritance+Composition
Delegation+Inheritance
Observer模式
composite模式
prototype模式
对象模型
vptr和vtbl
静态绑定:call XXX
动态绑定满足3个条件:
- 通过指针
- 向上转型
- 调用虚函数
由于指针不确定,所以称为动态绑定。
看图中3个类的继承关系A<-B<-C, A中有两个虚函数,两个非虚函数。 B继承了A,所以也有两个虚函数,但是对其中一个虚函数进行了重写。同样C也是。所以这里一共有8个函数。如图所示。这里对虚函数的继承是指的对调用权的继承,具体的机制是,每个类都有一个虚指针,指向一个虚表,虚表中存放所有虚函数的地址。具体的调用形式见图上下方。
this指针
myDoc.OnFileOpen()
在调用的时候会传一个参数this,这里this指的就是myDoc这个子类的对象。然后调用OnFileOpen()
的时候会把这个指针传到函数中,这个函数中所有的调用都变成了this->XXX()
,当调用Serialize()
的时候,转换成(*(this-vptr)[n])(this)
这种形式,动态绑定子类的Serialize()函数。