类与类之间有三大关系:继承(Inheritance)、复合(Composition)和委托(Delegation)
3.1.1 复合(Composition)
template
class Sequence = deque > class queue {
. . .
protected:
Sequence c;
public:
bool empty() const { return c.empty(); }
size_type size() const { return s.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(); }
};
3.1.2(Adapter:)改造
//No:1
template
class queue{
...
protected c;//拥有模块2
...
};
//No:2
template
class queue{
protected:
Itr strat;//拥有模块3,sizeof:16
Itr finish;
T** map;//指针的指针,4个字节
unsigned int map_size;//4个字节
};
//No:3
template
struct Itr{
T* cur;//sizeof:4*4
T* first;
T* last;
T** node;
...
};
3.1.3复合关系下的构造函数和析构函数:
构造由内而外:Container::Component(...): Component() {...};
Container的构造函数首先调用Component的default构造然后才执行自己
析构由外而内:Container :: ~Container(...) { ... ~Component() }
Container的析构函数首先执行自己,然后才调用Component的析构函数
3.2 委托:两个类之间相连
class String{
public:
String();
~String();
String(const char*s);
String(const String& a);
String &operator=(const String& s);
private:
StringRep* rep;//等到需要用到StringRep时才去创建StringRep类
};
class StringRep{
friend class String;
StringRep(const char* s);
~StringRep();
int count;
char* rep;
}
3.3.1 继承:
struct _List_node_base
{
_List_node_base* _M_next:
_List_node_base* _M_prev;
};
template
struct _List_node :public _List_node_base
{
_Tp _M_data;//它除了拥有自己的东西以外,还拥有基类的两个东西
};
3.3.2基类的析构函数必须是virtual,否则会出现undefined behavior
在继承的情况下,构造由内而外:Derived::Derived(...) : Base() {...};
Derived的构造函数首先调用基类的默认构造函数,然后才执行自己
析构由外而内:Derived的析构函数首先执行自己,然后才调用基类的析构函数
Derived::~Derived(...){... ~Base() };
3.3.3
non-virtual函数:你不希望derived class 重新定义它
virtual函数:你希望derived class重新定义它,且它已有默认定义。
pure virtual函数:你希望derived class一定要重新定义它,你对他没有默认定义。
3.4 纯虚函数:virtual 函数类型 函数名(参数表)=0;
声明为纯虚函数后,基类中就不再给出函数的实现部分,且纯虚函数不具备函数的功能,不能被调用。
作用:在基类中为其派生类保留一个函数的名字。以便派生类根据需要对它进行重载
3.5. inheritance+Composition关系下的构造和析构:
派生类中有基类的part,但派生类中又有Component,这时,调用顺序是基类=Component.>派生类
派生类中有基类的part,而基类part中又有Component时,这时调用顺序为:Component>基类>派生类,析构函数正好相反
委托+继承:
class Subject{
int m_value;
vector m_views;//容器里的类型为Observe*
public:
void attach(Observe* obs)
m_views.push_back(obs);
void set_val(int value)
{
m_value = value;
motify();
}
void notify()
{
for(int i=0;i
m_views[i]->update(this,m_value);
}
};
class Observe{
public:
virtual void update(Subject* sub,int value)const =0;
};
委托+继承:
容器里面的东西一定要放一样的大小,指针是最优选
变量名:partname
#nclude
enum imageType{LAST,SPOT};//枚举
class Image{
public:
virtual void draw()=0;
static Image* findAndClone(imageType);
protected:
virtual imageType return Type()=0;
virtual Image* clone() =0;//要求子类必须对其进行编译
static void assProtype(Image *image)//静态成员函数
{
_prototypes[_nextSlot++]
}
private:
static Image* __prototypes[10];
static int _nextSlot;//class当中的静态date,一定要在这个类外做一次定义,分配内存
};
Image *Image::_prototypes[];
int Image::_nextSlot;
Image *Image::findAndClone(imageType type)
{//当下面所有的子类把自己的一份原型放上去之后填充的数组
for(int i=0;i<_nextSlot;i++)
{
if(_prototypes[i]->() ==type)//找到一个原型之后,调用clone(),就做了一个副本
return _prototypes[i]->clone;
}
public继承和is-a之间的等价关系听起来颇为简单,但有时候会误导人
企鹅是一种鸟,这是对的;鸟会飞,这也对的。但企鹅会飞吗?
class Bird{
public:
virtual void fly();//鸟会飞
...
};
class Penguin:public Bird{//企鹅是一种鸟
...
};
这个代码但是是行得通的,但是那不是真的。
上述例子中,有一个与事实不符的error。如果谨慎一点,应该是:有的鸟会飞,有点鸟不会飞。来塑模出较佳的真实性:
class Bird{
public:
...//没有声明fly()函数
};
class FlyingBird:public Bird{
public:
virtual void fly();//鸟会飞
...
};
class Penguin:public Bird{//企鹅是一种鸟
...
};
这样的继承体系才能比原先的设计更能忠实反映出真正的意思。
现在,如果非得要求企鹅会飞,那编译器会不满:
Penguin p;
p.fly();//错误
一个好的接口可以防止无效的代码通过编译,因此应该采取“在编译期拒绝企鹅飞行”的设计,而不是“只在运行期间才能侦测它们”的设计。
所以,is-a并不是唯一存在于类之间的关系。