目标学习class之间的关系
- 继承 inheritance
- 复合 composition
- 委托 delegation
Object Oriented Programming, Object Oriented Designed
OOP, OOD
面向对象的编程
组合与继承
Inheritance(继承)
Composition(复合)
Delegation(委托)
1.1 Compositon(复合),表示has-a
一个类中,包含了另一个类,就叫复合。生命周期同步。
如下,queue类里面包含了Sequence类。
(1)queue has a deque(实心代表has-a复合)
Composition(复合),表示has-a 设计模式是:Adapter适配器 Adapter:queue has deque, 它所有的接口都可以用deque实现
template <class T>
class queue
{ ...
protected:
deque<T> c; //底层容器 queue has a deque public: // 以下完全利用c的操作函数完成
bool empty() const
{
return c.empty();
}
size_type size() const
{
return c.size();
}
reference front()
{
return c.front();
}
reference back()
{
return c.back();
}
void push(const value_type& x)
{
c.push_back(x);
}
void pop()
{
c.pop_front();
}
};
(2)从内存的角度分析Compositon
(3)Compositon(复合)关系下的构造和析构
构造由内而外(这样结构才稳定)
Container的构造函数首先调用Componet的default构造函数,然后才执行自己
Container::Container(...):Component(){...}; //先Component()后Container()
析构由外而内(从外部开始)
Container的析构函数首先执行自己,然后才调用Compositon的析构函数。
Container::~Container(...){...执行Container的析构操作 ~Component(); }
1.2 Delegation(委托). Compositon by reference
一个类,仍然包含另一个类,但是不是通过内存直接包含,而是用一个指针包含。
生命周期不同步(String先创建,有需要时才创建stringRep)。
如下,String类委托了一个StringRep类。
委托可以对外接口一致,String接口永远不变,但是内部实现可以通过修改StringRep改变。
有a、b、c 3个变量在使用同一个字符串hello, 采用引用计数
如果a要改变hello,但不可以影响b、c,采用copy on write, 先复制一份然后修改
//file String.hpp class StringRep;
class String
{
public:
String();
String(const char* s);
String(const String& s);
String &operator=(const String& s);
~String(); ...
private:
StringRep* rep; //Composition by reference pimpl
};
//file String.cpp
#include <String.hpp>
namespace
{
class StringRep
{
friend class String;
StringRep(const char* s);
~StringRep();
int count;
char* rep;
};
}
String::String() { ... }
...
Pimpl(Pointer to implementation) 是一种减少代码依赖和编译时间的C++编程技巧,其基本思想是将一个外部可见类(visible class)的实现细节(一般是所有私有的非虚成员)放在一个单独的实现类(implementation class)中,而在可见类中通过一个私有指针来间接访问该实现类。
Pimpl(Pointer to implementation) 是一种减少代码依赖和编译时间的C++编程技巧,其基本思想是将一个外部可见类(visible class)的实现细节(一般是所有私有的非虚成员)放在一个单独的实现类(implementation class)中,而在可见类中通过一个私有指针来间接访问该实现类。
1.3 Inheritance(继承),表示is-a
(1)使用public继承,就是在传达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; };
(2)继承关系下的构造和析构
Base class的析构函数通常设置为virtual ,这样可以保证析构时由外向内。
构造由内而外
Derived的构造函数首先调用Base的default构造函数,然后执行自己。
Derived::Derived(...): Base(){ ... };
析构由外而内
Derived的析构函数首先执行自己,然后才调用Base的析构函数。
Derived::~Derived(...) {... ~Base() };
1.3 表示is-a关系,Inheritance(继承)with virtual functions(虚函数)
(1) 当我我们使用继承时,是要搭配虚函数来使用,才会产生强而有力的效果。
non-virtual函数:你不希望derived class 重新定义(override,覆写)它
virtual 函数:你希望derived class 重新定义(override覆写)它,且你对它已有默认定义。
pure virtual 函数:你希望derived class一定要重新定义(override覆写)它,但是没有默认定义。
虚函数重新定义才叫override,覆写
class Shape
{
public:
virtual void draw() const = 0; //pure virtual //子类必须重新定义
virtual void error(const std::string& msg); //impure virtual//子类可以重新定义
int objectID() const; //non-virtual //不希望子类重新定义
};
//继承
class Rectangle:public Shape {...};
class Ellipse:public Shape {...};
(2) Template Method(Inheritance with virtual)
#include <iostream>
using namespace std;
class CDocument
{
public:
void OnFileOpen()
{ //这是个算法,每个cout输出代表一个实际动作
cout<<"dialog..."<<endl;
cout<<"check file status..."<<endl;
cout<<"open file..."<<endl;
Serialize();
cout<<"close file..."<<endl; cout<<"update all views..."<<endl;
}
virtial void Serialize(){ };
};
class CMyDoc : public CDocument
{
public:
virtual void Serialize()
{ //只有应用程序本身才知道如何读取自己的文件(格式)
cout<<"CMyDoc::Serialize()"<<endl;
}
};
int main()
{
CMyDoc myDoc; //假设对应[File/Open]
myDoc.OnFileOpen();
}
(3) Inheritance + Compositon关系下的构造和析构
(4) Delegation(委托) + Inheritance(继承)
文件只有一份,有多种呈现形式,Observer可以被继承
实例一:
class Subect
{
int m_value; //要被观察的数据
vector<Observer*> m_views; //委托 //观察者列表
public:
void attach(Observer* obs)
{
m_views.push_back(obs);
} //修改值之后,要通知所有的Observer,让它们做出改变
void set_val(int value)
{
m_value = value; notify();
}
void notify()
{
for(int i=0; i<m_views.size(); ++i)
m_views[i]->update(this,m_value); //调用Obeserver的方法,去更新Obeserver的显示。把自己指针传出去
}
};
class Observer //作为基类被多个观察者或显示着继承
{
public:
virtual void update(Subject* sub,int value) = 0; //纯虚函数,让子类的观察者真正去显示这个值
};
class Observer1 : public Observer
{
int m_div;
public:
Observer1(Subject* model, int div)
{
model-attach(this);
m_div = div;
} /*virtual*/ void update(int v) { ... }
};
class Observer2 : public Observer
{
int m_mod; public:
Observer2(Subject* model,int mod)
{
model->attach(this);
m_mod = mod;
} /*virtual*/
void update(int v)
{ ... }
};
int main()
{
Subject subj;
Observer1 o1(&subj,4);
Observer2 o2(&subj,3);
Observer3 o3(&subj,3);
subj.set_val(14);
}
实例二:
class Component {
int value;
public: Component(int val)
{ value = val; }
virtual void add(Component*) { }
};
class Primitive : public Component
{
public: Primitive(int val) : Component(val) {}
};
classs Composite : public Component
{
vector<Component*> c;
public:
Composite(int val) : Component(val) { }
void add(Component* elem)
c.push_back(elem);
};
实例三:父类想创建未来才定义的子类。
我需要一个树状继承体系,我需要创建未来才能出现的子类,设计一个框架,未来客户买去才会创建。
我不知道未来的子类是什么,那我怎么创建。
让派生的子类都创建一个子类,让父类框架可以看到,我就可以Copy。