C++ 类和类之间的关系(继承,组合,聚合)

主要介绍一下类与类之间的关系,也就是面向对象编程先介绍两个术语

  • Object Oriented Programming   OOP面向对象编程
  • Object Oriented Design  OOD面向对象设计
  • 对于类与类之间的关系有很多种,但是我认为理解3种足够
  1. Inheritance (继承)
  2. Composition (组合) 
  3. Delegation (委託)  该种关系也可以理解成聚合

 

一:组合 Composition

1.定义: has-a的关系,一个类中有包含另外一个类 (类中的成员变量之一是类),是包含一个对象,而不是包含一个指针,如果你组合了这个类,那么你就将拥有你包含的类的全部功能

  • 下面我介绍一个组合的实际应用
#include
#include 
template 
class queue {
    ...
protected:
    std::deque c; // 底層容器       has-a的关系
public:
    // 以下完全利用 c 的操作函數完成
    bool empty() const { return c.empty(); }//利用deque的功能来实现queue新定义的功能
    size_t 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(); }
};
  • queue是一种队列操作,单方向操作先进先出
  • deque是两端都可进出,所以说deque的功能较强大与quque,但是如果我queue组合deque(包含 has-a)那么我们就可以利用deque的功能来实现queue新定义的功能
  • 这就是组合关系的一种实际应用,同时它也是adapter(适配器)设计模式

2.用类图表示has-a的模式,Container(容器)  Component(组成部分 part) Container has Component

  • 那么上面的queue与deque的类图为

queue包含deque


3.has-a composition 内存管理

template 
class queue {
protected: 
    deque c; 
    ... 
};


template 
class deque {
protected:
    Itr start; //16 bit
    Itr finish;  //16 bit
    T** map; //4bit
    unsigned int map_size;  //4bit
};


template 
struct Itr { struct Itr {
    T* cur;   //4bit
    T* first; 
    T* last; 
    T** node; 
    ...
};

C++ 类和类之间的关系(继承,组合,聚合)_第1张图片

所以是queue的内存为40bit


4.构造与析构

  • 未了方便我们的理解,我们可以将组合关系联想成下图

C++ 类和类之间的关系(继承,组合,聚合)_第2张图片

  • 构造由内而外

Container 的构造函数首先调用 Component 的 default 构造函数,然後才执行自己 的构造函数,可以理解成

如果不要使用Commponent的default构造函数函数,就必须显式调用Component的构造函数

 Container::Container(...): Component() { ... }; 
  • 析构由外而内

Container 的析构函数首先执行自己的,然后调用 Component 的 析构函数,可以理解成这样

 Container::~Container(...){ ... ~Component() };
  • 生命周期

 Container于Component具有相同的生命周期


二.聚合 也就是委托关系

1.定义has-a pointer,一个类中包含另一个类的指针,你也同样拥有被包含类的全部功能,他有一个重要的使用方法handle/body(pImpl)

class StringRep;

class String {//handle
public:
    String();
    String(const char* s);
    String &operator=(const String& s); ~String();
    ....
private:
    StringRep* rep; // pimpl
};


class StringRep { //body
    friend class String;
    StringRep(const char* s);
    ~StringRep();
    int count;
    char* rep;
};

 功能其实与组合非常相似

2.类图

3.内存管理

  • 包含一个指针    4bit

4.构造与析构

  • 不发生影响

5.生命周期

  • 生命周期可以不相同

三.继承

1.定义is-a的关系,分为父类(Base)和子类(Drived),可以理解成孩子继承父亲的财产,就是父类有的子类都可以有,也可以理解成子类有父类的成分

class _List_node_base
{
    ...
    _List_node_base* _M_next;
    _List_node_base* _M_prev;
    ...
};

template
class _List_node: public _List_node_base
{
    _Tp _M_data;
};

2.类图

C++ 类和类之间的关系(继承,组合,聚合)_第3张图片

3.内存管理 

  • 无太大关联,抛去成员变量,子类比父类多一个虚函数表 4bit

4.构造与析构

  • 子类含有父类的成分,可以理解成

C++ 类和类之间的关系(继承,组合,聚合)_第4张图片

  • 构造由内而外
    • Derived 的构造函数首先调用Base 的 default 构造函数, 然后执行自己的
Derived::Derived(...): Base() { ... }; 
  • 析构由外而内
    • Derived 的析构函数首先执行自己的,然后调用用 Base 的析构函数。 
Derived::~Derived(...){ ... ~Base() }; 

 5.继承真正的使用是与虚函数的搭配

  • 虚函数:用virtual声明的函数,它有三种形式
  1. non-virtual  即普通函数,你不希望子类重新定义它(重新定义override)
  2. virtual 函数(虚函数):你希望 derived class 重新定义 它,且你对这个函数有默认定义
  3. pure virtual 函数(纯虚函数):你希望 derived class 一定要重新定义它,你对它没有默认定义
void func_1();//non-virtual
virtual void func_2();//virtual
virtual void func_3() = 0;//pure virtual

四 .组合+继承

  1. 组合和继承共同使用它们的它们的创建顺序会是什么
  2. 两种状态,第一种比较好理解

  • Component构造 > Base构造 > 子类构造  析构相反

C++ 类和类之间的关系(继承,组合,聚合)_第5张图片

  • Base构造 > Component构造 > 子类构造  析构相反
  • Derived 的构造函数首先调用 Base 的 default 构造函数, 然后调用 Component 的 default 构造函数, 然后执行自己 Derived::Derived(...): Base(),Component() { ... }; 
  • Derived 的析构函数首先执行自己, 然后调用 Component 的 析构函数,然后調用 Base 的析构函数 Derived::~Derived(...){ ... ~Component(), ~Base() }; 

sample code

#include 
using namespace std;

class Base{
private:
	int B_num;
public:
	Base(int val):B_num(val){
		cout << "This is Base !!! "<<  B_num << endl;
	}
	void V_test()
	{
		cout <<"This is V_test!!"<

 

你可能感兴趣的:(C++)