C++面向对象编程与类之间的关系(继承、复合、委托)
1、复合(Composition)
2、继承(Inheritance)
3、委托 (Delegation)
4、虚函数
一、复合(Composition)表示 has-a
示例代码:
template <class T, class Sequence = deque<T> >
class queue {
...
protected:
Sequence c; // 底层的容器
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(); }
// deque 是兩端可進出,queue 是末端進前端出(先進先出)
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
}
我们分析上面一段代码,队列queue中有一个双端队列Sequence对象,我们称这种关系为复合。队列中大部分的函数底层的实现我们是复用的双端队列对象Sequence的函数。
分析复合Composition下的构造和析构
构造由内而外:queue首先调用的是Sequence的默认的构造函数,然后再执行自己的构造函数。从内部到外部才扎实。
析构由外而内:queue先执行自己,然后再调用Sequence对象的析构函数。
从内存的角度看:queue对象的内存大小为类Sequence所占的内存的大小。
2、委托 (Delegation) Composition by reference.
示例代码:
// file String.cpp
#include "String.hpp"
namespace {
class StringRep {
friend class String;
StringRep(const char* s);
~StringRep();
int count;
char* rep;
};
}
String::String(){ ... }
...
//fileOne.hpp
class StringRep;
class String {
public:
String();
String(const char* s);
String(const String& s);
String &operator=(const String& s);
~String();
. . . .
private:
StringRep* rep; // pimpl
}
我们从上述代码可以得出,一个对象数据成员中包含另一个Class的指针,我们称这种关系为委托。
对外开出接口真正的实现可以再我们成员对象中去实现,编译防火墙,接口不改变,真正的实现可以变化。
3、继承(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;
};
子类对象拥有父类对象的成员函数和成员对象,子类对象里有父类的成分。
分析继承下的构造和析构
构造由内而外:先调用父类的默认构造函数,再执行自己
析构由外而内:先调用自己的析构函数,再调用父类的构造函数。
举一个例子:
class CDocument
{
public:
void OnFileOpen()
{
... //一系列固定得操作
Serialize();
... //一系列固定得动作
}
virtual void Serialize() { };
};
class CMyDoc : public CDocument
{
public:
virtual void Serialize()
{
//独有得读取文件格式的方式
cout << "CMyDoc::Serialize()" << endl;
}
};
int main()
{
CMyDoc myDoc; // 调用文件的方式[File/Open]
myDoc.OnFileOpen();
return 0;
}
考虑继承加上复合情况下的构造和析构?
构造由内而外:先调用父类的默认构造函数,然后调用子类成员的构造函数,再执行自己
析构由外而内:先调用自己的析构函数,然后调用子类成员的析构函数 最后调用父类的构造函数。
4、虚函数
1、non-virtual 普通函数:你不希望子类重新定义该函数 (override, 重写) 它.
2、virtual 虚函数:你希望子类重新定义(override, 重写) 它,且你已经对它头默认的定义。
3、pure virtual 纯虚函数:你希望 子类一定要重新定义(override, 重写) 它,你对它没有默认的定义。
示例代码如下:
class Shape {
public:
virtual void draw( ) const = 0; //纯虚函数
virtual void error(const std::string& msg); //虚函数
int objectID( ) const; //普通函数
...
};
注意:较为复杂的情况,委托加上继承
示例一个观察者模型例子:
class Subject
{
private:
int m_value;
vector<Observer*> m_views;
public:
void attach(Observer* obs)
{
m_views.push_back(obs);
}
void set_val(int value)
{
m_value = value;
notify();
}
void notify()
{
for (int i = 0; i < m_views.size(); ++i)
m_views[i]->update(m_value);
}
}
class Observer
{
public:
virtual void update(int value) = 0;
...
};
class Observer1: public Observer
{
public:
Observer1(Subject *model, int div)
{
model->attach(this);
m_div = div;
}
/* virtual */void update(int v)
{ … }
...
};
class Observer2: public Observer
{
public:
Observer1(Subject *model, int div)
{
model->attach(this);
m_div = div;
}
/* virtual */void update(int v)
{ … }
...
};
{
Subject subj; //调用
Observer1 o1(&subj, 4);
Observer1 o2(&subj, 3);
Observer2 o3(&subj, 3);
subj.set_val(14);
}
//此例子试用于多视图现实的时候可以使用,想想是不是很符合场景
委托加上继承还有很多设计,这里不一一去书写了,后面的文章中会写到,加油又是干劲十足的一天。