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

2023-10-20 08:40

本文主要是介绍C++ 类和类之间的关系(继承,组合,聚合),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

 

一:组合 Composition

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

  • 下面我介绍一个组合的实际应用
#include<deque>
#include <queue>
template <class T>
class queue {...
protected:std::deque<T> 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 T>
class queue {
protected: deque<T> c; ... 
};template <class T>
class deque {
protected:Itr<T> start; //16 bitItr<T> finish;  //16 bitT** map; //4bitunsigned int map_size;  //4bit
};template <class T>
struct Itr { struct Itr {T* cur;   //4bitT* first; T* last; T** node; ...
};

所以是queue的内存为40bit


4.构造与析构

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

  • 构造由内而外

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 { //bodyfriend 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<typename _Tp>
class _List_node: public _List_node_base
{_Tp _M_data;
};

2.类图

3.内存管理 

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

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构造 > 子类构造  析构相反

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

sample code

#include <iostream>
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!!"<<endl;Base_print();}virtual void Base_print(){}~Base(){cout << "Destructor Base !!!" <<endl;}};class Other{private:int O_num;public:Other(int val):O_num(val){cout << "This is Other!!! " << O_num <<endl;}Other(){cout << "This is Other default !!!" <<endl;}~Other(){cout << "Destructor Other !!!" <<endl;}void Other_Pint(){cout <<"This is Other_Print!!!"<<endl;};
};//inheritance  derive
class Child : public Base{private:int C_num;Other O_Child;//compositionpublic:	Child(int val,int bval):Base(bval),C_num(val),O_Child(val){O_Child.Other_Pint();cout << "This is Child !!! " << C_num << endl;}void Base_print(){cout <<"This is Child functiong,not Base virtual functiong!!!" <<endl;};~Child(){cout << "Destructor Child !!!" <<endl;}
};int main()
{Child cc(30,50);cc.V_test();
//	cc.Other_Pint();  //error: ‘class Child’ has no member named ‘Other_Pint’return 0;
}
/*
This is Base !!! 50
This is Other!!! 30
This is Other_Print!!!
This is Child !!! 30
This is V_test!!
This is Child functiong,not Base virtual functiong!!!
Destructor Child !!!
Destructor Other !!!
Destructor Base !!!*/

 

这篇关于C++ 类和类之间的关系(继承,组合,聚合)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/245984

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

Java文件与Base64之间的转化方式

《Java文件与Base64之间的转化方式》这篇文章介绍了如何使用Java将文件(如图片、视频)转换为Base64编码,以及如何将Base64编码转换回文件,通过提供具体的工具类实现,作者希望帮助读者... 目录Java文件与Base64之间的转化1、文件转Base64工具类2、Base64转文件工具类3、

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、