• 10、C++设计模式与泛型编程



    一、软件设计模式

    • 模式:一个模式描述了一个不断发生的问题及这个问题的解决方案;模式是前人的设计经验上总结出来的对于一些普遍存在的问题提供的通用解决方案;比如单例模式,观察者模式等
    • 软件中的模式:面向对象常用的23种设计模式、反模式及企业应用架构模式等
    • 推荐书籍:《设计模式:可服用面向对象软件的基础(GOF)》

    二、单例模式(Singleton)

    • 什么是单例:有些时候,我们需要整个程序中有且仅有一个实例

    • 单例模式常见场景:如系统日志、Windows资源管理器,数据库分配主键操作等
      在这里插入图片描述

    • 实现思路

      • ①.Singleton拥有一个私有构造函数,确保用户无法通过new直接实例它
      • ②.包含一个静态私有成员变量instance与静态公有方法Instance();

    Singleton.h

    #pragma once
    
    #include <iostream>
    using namespace std;
    class Singleton
    {
    public:
    	static const Singleton* getInstance();
    	static void DoSomething()
    	{
    		cout << "Do Something" << endl;
    	}
    private:
    	// 将构造和析构函数私有化,防止外部访问
    	Singleton();
    	~Singleton();
    
    	static Singleton* This; // 使用静态变量帮助解决资源的分配和释放
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Singleton.cpp:这里如果将Singleton* Singleton::This = nullptr;修改为Singleton* Singleton::This = new Singleton();就是饿汉式单例

    #include "Singleton.h"
    
    
    Singleton* Singleton::This = nullptr;
    
    const Singleton* Singleton::getInstance()
    {
    	if (!This)
    	{
    		This = new Singleton;
    	}
    	return This;
    }
    
    Singleton::Singleton()
    {
    }
    
    Singleton::~Singleton()
    {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Test.cpp

    #include <iostream>
    #include "Singleton.h"
    using namespace std;
    
    
    int main()
    {
    	Singleton::getInstance()->DoSomething();
    	Singleton::getInstance()->DoSomething();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    三、观察者模式(Observer)

    • 什么是观察者模式:观察者需要直接订阅目标时间;在目标发出内容改变的事件后,直接接收事件并作出相应,对象常是一对多关系
    • 观察者模式常用场景:各种MVC的框架中,Model的变化通知,各种类型的View几乎都存在这种模式
    • 观察者模式实现思路:将问题的职责解耦合,将Observable和Observer抽象开,分清抽象和实体

    Observer.h

    #pragma once
    
    # ifndef OBSEVER_H_1
    # define OBSEVER_H_1
    class Observer
    {
    public:
    	Observer() { ; }
    	virtual ~Observer() { ; }
    
    	// 当被观察对象发生变化时,通知被观察者调用这个方法
    	virtual void Update(void* pArg) = 0;
    };
    # endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Observerable.h

    #pragma once
    
    class Observer;
    
    #include <string>
    #include <list>
    #include "Observer.h"
    
    using namespace std;
    class Observerable
    {
    public:
    	Observerable();
    	virtual ~Observerable();
    
    	// 注册观察者
    	void Attach(Observer* pOb);
    	// 反注册观察者
    	void Detach(Observer* pOb);
    
    	int GetObseverCount() const
    	{
    		return _Obs.size();
    	}
    
    	void DetachAll()
    	{
    		_Obs.clear();
    	}
    
    	virtual void GetSomeNews(string str)
    	{
    		SetChange(str);
    	}
    protected:
    	void  SetChange(string news);   // 有变化,需要通知
    
    private:
    	void Notify(void* pArg);
    
    private:
    	bool _bChange;	//表示当前状态
    	list<Observer*> _Obs;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    Observerable.cpp

    #include "Observerable.h"
    
    
    Observerable::Observerable() :_bChange(false)
    {
    
    }
    
    
    Observerable::~Observerable()
    {
    }
    
    
    // 注册观察者
    void Observerable::Attach(Observer* pOb)
    {
    	if (pOb == NULL)
    	{
    		return;
    	}
    
    	// 看看当前列表中是否有这个观察者
    	auto it = _Obs.begin();
    	for (; it != _Obs.end(); it++)
    	{
    		if (*it == pOb)
    		{
    			return;
    		}
    	}
    
    	_Obs.push_back(pOb);
    }
    
    // 反注册观察者
    void Observerable::Detach(Observer* pOb)
    {
    	if ((pOb == NULL) || (_Obs.empty() == true))
    	{
    		return;
    	}
    
    	_Obs.remove(pOb);
    }
    
    void Observerable::SetChange(string news)
    {
    	_bChange = true;
    
    	Notify(((void*)news.c_str()));
    }
    
    
    void Observerable::Notify(void* pArg)
    {
    	if (_bChange == false)
    	{
    		return;
    	}
    
    	// 看看当前列表中是否有这个观察者
    	auto it = _Obs.begin();
    	for (; it != _Obs.end(); it++)
    	{
    		(*it)->Update(pArg);
    	}
    
    	_bChange = false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    Test.cpp

    #include <iostream>
    #include "Observer.h"
    #include "Observerable.h"
    using namespace std;
    
    //这个是被观察者
    class News : public Observerable
    {
    public:
    	virtual void GetSomeNews(string str)
    	{
    		SetChange("News: " + str);
    	}
    };
    
    //观察者1
    class User1 :public Observer
    {
    public:
    	virtual void Update(void* pArg)
    	{
    		cout << "User1 Got News: " << reinterpret_cast<char*>(pArg) << endl;
    	}
    };
    
    //观察者2
    class User2 :public Observer
    {
    public:
    	virtual void Update(void* pArg)
    	{
    		cout << "User2 Got News: " << reinterpret_cast<char*>(pArg) << endl;
    	}
    };
    
    int main()
    {
    	User1 u1;
    	User2 u2;
    
    	News n1;
    	n1.GetSomeNews("T0");
    	cout << n1.GetObseverCount() << endl;  // 0
    
    	n1.Attach(&u1);
    	n1.Attach(&u2);
    	n1.GetSomeNews("T1");
    	cout << n1.GetObseverCount() << endl;  // 2
    
    	n1.Detach(&u2);
    	n1.GetSomeNews("T2");
    	cout << n1.GetObseverCount() << endl;  // 1
    
    	n1.DetachAll();
    	n1.GetSomeNews("T3");
    	cout << n1.GetObseverCount() << endl;  // 0
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    四、适配器模式(Adapter)

    • 适配器简介
      • 适配器将类接口转换为客户端期望的另一个接口
      • 使用适配器可防止类由于接口不兼容而一起工作
      • 适配器模式的动机是,如果可以更改接口,则可以重用现有软件

    1 - 多重继承实现Adapter

    #include <iostream>
    using namespace std;
    
    
    class LegacyRectangle
    {
    public:
    	LegacyRectangle(double x1, double y1, double x2, double y2)
    	{
    		_x1 = x1;
    		_y1 = y1;
    		_x2 = x2;
    		_y2 = y2;
    	}
    
    	void LegacyDraw()
    	{
    		cout << "LegacyRectangle:: LegacyDraw()" << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
    	}
    
    private:
    	double _x1;
    	double _y1;
    	double _x2;
    	double _y2;
    };
    
    class Rectangle
    {
    public:
    	virtual void Draw(string str) = 0;
    };
    
    // 第一种适配的方式:使用多重继承
    class RectangleAdapter : public Rectangle, public LegacyRectangle
    {
    public:
    	RectangleAdapter(double x, double y, double w, double h) :
    		LegacyRectangle(x, y, x + w, y + h)
    	{
    		cout << "RectangleAdapter(int x, int y, int w, int h)" << endl;
    	}
    
    	virtual void Draw(string str)
    	{
    		cout << "RectangleAdapter::Draw()" << endl;
    		LegacyDraw();
    	}
    };
    
    
    int main(void)
    {
    	double x = 20.0, y = 50.0, w = 300.0, h = 200.0;
    	RectangleAdapter ra(x, y, w, h);
    	Rectangle* pR = &ra;
    	pR->Draw("Testing Adapter");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    2 - 组合方式实现Adapter

    #include <iostream>
    using namespace std;
    
    
    class LegacyRectangle
    {
    public:
    	LegacyRectangle(double x1, double y1, double x2, double y2)
    	{
    		_x1 = x1;
    		_y1 = y1;
    		_x2 = x2;
    		_y2 = y2;
    	}
    
    	void LegacyDraw()
    	{
    		cout << "LegacyRectangle:: LegacyDraw()" << _x1 << " " << _y1 << " " << _x2 << " " << _y2 << endl;
    	}
    
    private:
    	double _x1;
    	double _y1;
    	double _x2;
    	double _y2;
    };
    
    class Rectangle
    {
    public:
    	virtual void Draw(string str) = 0;
    };
    
    // 组合方式的Adapter
    class RectangleAdapter2 :public Rectangle
    {
    public:
    	RectangleAdapter2(double x, double y, double w, double h) :
    		_lRect(x, y, x + w, y + h)
    	{
    		cout << "RectangleAdapter2(int x, int y, int w, int h)" << endl;
    	}
    
    	virtual void Draw(string str)
    	{
    		cout << "RectangleAdapter2::Draw()" << endl;
    		_lRect.LegacyDraw();
    	}
    private:
    	LegacyRectangle _lRect;
    };
    
    
    int main(void)
    {
    	double x = 20.0, y = 50.0, w = 300.0, h = 200.0;
    	RectangleAdapter2 ra2(x, y, w, h);
    	Rectangle* pR2 = &ra2;
    	pR2->Draw("Testing2 Adapter");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    五、泛型编程

    1 - 泛型编程思想

    • 泛型编程概述
      • 如果说面向对象是一种通过间接层来调用函数,以换取一种抽象,那么泛型则是更直接的抽象,它不会因为间接层而损失效率
      • 不同于面向对象的动态期多态,泛型编程式一种静态期多态,通过编译器生成最直接的代码
      • 泛型编程可以将算法与特定类型,结构剥离,尽可能复用代码
      • 模板编程的难点很大程度上在于对编译器的理解,我们需要知道怎么帮助编译器提供需要生成代码的信息

    2 - 泛型编程的简单例子

    #include <iostream>
    using namespace std;
    
    template<class T>
    T getMax(T a, T b)
    {
    	return a > b ? a : b;
    }
    
    int main(void)
    {
    	// 模板函数的测试
    	cout << getMax(1, 2) << endl;
    	cout << getMax(1.5, 3.5) << endl;
    	cout << getMax('a', 'b') << endl; //使用ASCII大小来比较
    	cout << getMax("hello", "world") << endl;//hello
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3 - 泛型编程特化

    • 我们希望char*的比较根据我们自己的规则来运算,这时候就可以特例化
    • 类型不一致的特化,假设需求我们知道返回int型,但是参数1和参数2都是不确定的类型,可能相同也可能不同
    #include <iostream>
    using namespace std;
    
    template<class T>
    T getMax(T a, T b)
    {
    	return a > b ? a : b;
    }
    
    //特化
    template<>
    char* getMax(char* a, char* b)
    {
    	return (strcmp(a, b) > 0 ? (a) : (b));
    }
    
    //类型不一致的特化
    template<class T1, class T2>
    int getMax(T1 a, T2 b)
    {
    	return static_cast<int>(a > b ? a : b);
    }
    
    int main(void)
    {
    	// 模板函数的测试
    	cout << getMax(1, 2) << endl;
    	cout << getMax(1.5, 3.5) << endl;
    	cout << getMax('a', 'b') << endl; //使用ASCII大小来比较
    	cout << getMax("hello", "world") << endl;
    
    	const char* s1 = "hello";
    	const char* s2 = "world";
    	cout << getMax(const_cast<char*>(s1), const_cast<char*>(s2)) << endl;//world
    
    	cout << getMax(10, 2.5) << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述


    总结1:void*、NULL和nullptr

    • C语言中NULL一般都是用来处理空指针的#define NULL ((void*)0)
    • 在C++语言中的NULL定义成0了
    #ifndef NULL
    	#ifdef __cplusplus
    		#define NULL 0
    	#else
    		#define NULL((void*)0)
    	#endif
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • C++11标准规定nullptr用来替代(void*)0,而NULL则只表示0
    #include <iostream>
    using namespace std;
    
    void func(void* i)
    {
    	cout << "func(void* i)" << endl;
    }
    void func(int i)
    {
    	cout << "func(int i)" << endl;
    }
    
    int main()
    {
    	int* pi = NULL;
    	int* pi2 = nullptr;
    	char* pc = NULL;
    	char* pc2 = nullptr;
    	func(NULL);		// func(int i)
    	func(nullptr);	// func(void* i)
    	func(pi);		// func(void* i)
    	func(pi2);		// func(void* i)
    	func(pc);		// func(void* i)
    	func(pc2);		// func(void* i)
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    总结2:类型转换

    1 - C的类型转换

    • C的类型转换
      • 隐式类型转换:double f = 1.0 / 2;
      • 显示类型转换:double f = double(1) / double(2)
    • C类型转换的问题
      • 任意类型之间都可以转换,编译器无法判断其正确性
      • 难于定位:在源码中无法快速定位

    2 - C++的类型转换

    • const_cast:用于转换指针或引用,去掉类型的const属性
    • 常量折叠:常量折叠就是,在编译阶段,对该变量进行值替换,类似宏定义
      • 这也就是为什么,我们看到内存中已经将a修改为100了,但实际打印出来的仍然是10
    #include <iostream>
    using namespace std;
    
    int main()
    {
    	/ C++ const_cast
    	const int a = 10;
    	//int* pA = &a;	//"const int *" 类型的值不能用于初始化 "int *" 类型的实体
    
    	int* pA = const_cast<int*>(&a);
    	*pA = 100;
    
    	cout << a << endl; 		// 10
    	cout << *pA << endl;	// 100
    
    	cout << &a << endl;
    	cout << pA << endl;
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    • reinterpret_cast:很危险;重新解释类型,既不检查指向的内容,也不检查指针类型本身;但要求转换前后的类型所占用内存大小一致,否则将引发编译时错误
    #include <iostream>
    using namespace std;
    
    int Test()
    {
    	return 0;
    }
    
    int main()
    {
    	// C++ reinterpret_cast
    	typedef void(*FuncPtr) ();//这里定义的函数指针返回值是void类型
    	FuncPtr funcPtr;
    	//funcPtr = &Test; //不能将 "int (*)()" 类型的值分配到 "FuncPtr" 类型的实体
    
    	funcPtr = reinterpret_cast<FuncPtr>(&Test);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • static_cast:用于基本类型转换,有继承关系对象和类指针之间转换,由程序来确保转换是安全的,它不会产生动态转换的类型安全检查的开销
    int main()
    {
    	int ii = 5;
    	double dd = static_cast<double>(ii);//基本类型转换  int -> double
    	double dd2 = 5.6;
    	int ii2 = static_cast<int>(dd2);//基本类型转换  double -> int
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • dynamic_cast:只能用于含有虚函数的类,必须用在多态体系中,用于类层次间的向上和向下转化;向下转化时,如果是非法的对于指针返回NULL
    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
    	Base() : _i(0) { ; }
    	virtual void T() { cout << "Base:T" << _i << endl; }
    private:
    	int _i;
    };
    
    class Derived : public Base
    {
    public:
    	Derived() :_j(1) { ; }
    	virtual void T() { cout << "Derived:T" << _j << endl; }
    
    private:
    	int _j;
    };
    
    int main()
    {
    	Base cb;
    	Derived cd;
    	Base* pcb;
    	Derived* pcd;
    
    	// 子类--》 父类(向上)
    	pcb = static_cast<Base*>(&cd);
    	if (pcb == NULL)
    	{
    		cout << "unsafe static_cast from Derived to Base" << endl;
    	}
    	pcb = dynamic_cast<Base*>(&cd);
    	if (pcb == NULL)
    	{
    		cout << "unsafe dynamic_cast from Derived to Base" << endl;
    	}
    
    	// 父类--》 子类(向下)
    	pcd = static_cast<Derived*>(&cb);
    	if (pcd == NULL)
    	{
    		cout << "unsafe static_cast from Derived to Base" << endl;
    	}
    	pcd = dynamic_cast<Derived*>(&cb);
    	if (pcd == NULL)
    	{
    		cout << "unsafe dynamic_cast from Derived to Base" << endl;
    	}
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    3.2.5:VBA对单元格操作的引申
    【GO】LGTM_Grafana_gin_trace中间件(3)_代码实操
    Python爬虫|Scrapy 基础用法
    Linux下时间相关接口
    中序表达式转为后序表达式
    归并排序 & 逆序对
    为什么要使用微服务架构?
    程序化易程序分析之后,如何进入交易程序?
    Lombok工具 : 常用注解介绍 (全)
    Flink入门
  • 原文地址:https://blog.csdn.net/qq23001186/article/details/125439579