Bläddra i källkod

添加设计模式

usuiforhe 4 år sedan
förälder
incheckning
4908500a3c
6 ändrade filer med 1814 tillägg och 1 borttagningar
  1. 0 0
      cpp/C++11&14.md
  2. 0 0
      cpp/STL标准库.md
  3. 0 0
      cpp/内存机制.md
  4. 1094 0
      cpp/设计模式.md
  5. 691 0
      cpp/面向对象.md
  6. 29 1
      虚幻.md

+ 0 - 0
cpp/C++11&14.md


+ 0 - 0
cpp/STL标准库.md


+ 0 - 0
cpp/内存机制.md


+ 1094 - 0
cpp/设计模式.md

@@ -0,0 +1,1094 @@
+# 一些设计模式
+
+## 设计原则
+
+1. 依赖倒置原则
+	- 高层模块(稳定)不应该依赖于底层模块(不稳定),而这应该都依赖于抽象(稳定)
+	- 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)
+2. 开放封闭原则
+	- 对扩展开放,对更改封闭
+	- 类模块应该是可扩展的,但是不可修改的
+3. 单一职责原则
+	- 一个类应该仅有一个引起他变化的原因
+	- 变化的方向隐含着类的责任
+4. Liskov替换原则
+	- 子类必须能够替换他们的基类
+	- 继承表达类型抽象
+5. 接口隔离原则
+	- 不应该强迫客户程序依赖他们不用的方法
+	- 接口应该小而完备
+6. 优先使用对象组合,而不是类继承
+	- 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
+	- 继承在某种程度上破坏了封装性,子类父类耦合度很高
+	- 对象组合只要求被组合的独享具有良好定义的接口,耦合度低
+7. 封装变化点
+	- 使用封装来创建对象之间的分界曾,让设计者可以在分界一侧进行修改,不会对另一侧产生不良的印象,从而实现层次间的松耦合
+8. 针对接口编程,而不是针对实现变成
+	- 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
+	- 减少系统中各个部分的依赖关系,从而实现高内聚低耦合的类型设计方案
+
+## 模式分类
+
+- 目的
+	1. 创建型模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击
+	2. 结构型模式:通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击
+	3. 行为型模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击
+
+- 从范围来看:
+	1. 类模式处理类与子类的静态关系
+	2. 对象模式处理对象间的动态关系
+
+## 模式
+
+### Template-method 模式
+非常基础设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)
+为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构
+
+**“不要调用我,让我来调用你”**的反向控制结构是Template Method的典型应用
+
+在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法
+
+```cpp
+int Application::run()
+{
+    PVRFrameEnableControlWindow(false);
+    UINT TARGET_RESOLUTION = 1; // 1 millisecond target resolution
+    TIMECAPS tc;
+    UINT wTimerRes = 0;
+    if (TIMERR_NOERROR == timeGetDevCaps(&tc, sizeof(TIMECAPS)))
+    {
+        wTimerRes = std::min(std::max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
+        timeBeginPeriod(wTimerRes);
+    }
+
+    LARGE_INTEGER nLast;
+    LARGE_INTEGER nNow;
+
+    QueryPerformanceCounter(&nLast);
+
+    initGLContextAttrs();
+
+    if (!applicationDidFinishLaunching())
+    {
+        return 1;
+    }
+
+    auto director = Director::getInstance();
+    auto glview = director->getOpenGLView();
+
+    glview->retain();
+
+    LONGLONG interval = 0LL;
+    LONG waitMS = 0L;
+
+    LARGE_INTEGER freq;
+    QueryPerformanceFrequency(&freq);
+
+    while(!glview->windowShouldClose())
+    {
+        QueryPerformanceCounter(&nNow);
+        interval = nNow.QuadPart - nLast.QuadPart;
+        if (interval >= _animationInterval.QuadPart)
+        {
+            nLast.QuadPart = nNow.QuadPart;
+            director->mainLoop();
+            glview->pollEvents();
+        }
+        else
+        {
+            waitMS = (_animationInterval.QuadPart - interval) * 1000LL / freq.QuadPart - 1L;
+            if (waitMS > 1L)
+                Sleep(waitMS);
+        }
+    }
+    
+    if (glview->isOpenGLReady())
+    {
+        director->end();
+        director->mainLoop();
+        director = nullptr;
+    }
+    glview->release();
+    if (wTimerRes != 0)
+    {
+        timeEndPeriod(wTimerRes);
+    }
+    return 0;
+}
+
+#include "main.h"
+#include "AppDelegate.h"
+
+USING_NS_CC;
+
+int WINAPI _tWinMain(HINSTANCE hInstance,
+					   HINSTANCE hPrevInstance,
+                       LPTSTR    lpCmdLine,
+                       int       nCmdShow)
+{
+    UNREFERENCED_PARAMETER(hPrevInstance);
+    UNREFERENCED_PARAMETER(lpCmdLine);
+
+    // create the application instance
+    AppDelegate app;
+    return Application::getInstance()->run();
+}
+```
+
+> 这里为cocos2dx的main.cpp源码,这里AppDelegate很明显是cocos2dx框架提供的对象,我们要做的只是修改run执行的对象,对run不需要更改
+
+对该模式进行分析:`run()`方法是较为稳定的方法,开发者需要修改的只是`run()`中的`director->mainLoop()`  
+作为模板的框架提供相对静止的`run()`,作为开发者为框架提供比较动态的`mainloop()`,再有框架来调用即可  
+将框架内部实现细节对开发者隐藏,让开发者仅对自己需要注意的部分花费心思  
+
+### 策略模式
+
+在软件构建中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不适用的算法也是一种性能负担
+
+```cpp
+/**
+ * 常规写法
+ */
+
+enum CalculateType
+{
+    Tpye_1,
+    Tpye_1,
+    Tpye_1,
+};
+
+class CalculateClass
+{
+    CalculateType _type;
+
+  public:
+    void calculateFunc() 
+    {
+        switch (_type)
+        {
+        case CalculateType::Tpye_1:
+            // 算法1
+            break;
+        case CalculateType::Tpye_2:
+            // 算法2
+            break;
+        case CalculateType::Tpye_3:
+            // 算法3
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+```
+
+> 该方法虽然能够针对不同类型做不同处理,但是不符合开闭原则:对扩展开放对修改关闭,这里如果添加新的类型只能修改源码才能继续运行
+
+```cpp
+/**
+ * 策略模式 
+ */
+
+class CalculateCls
+{
+  public:
+    virtual int Calculate() = 0;
+    virtual ~CalculateCls() {}
+};
+
+class CalculateType_1 : public CalculateCls
+{
+  public:
+    virtual int Calculate() override
+    {
+        // 算法1
+    }
+};
+class CalculateType_2 : public CalculateCls
+{
+  public:
+    virtual int Calculate() override
+    {
+        // 算法2
+    }
+};
+class CalculateType_3 : public CalculateCls
+{
+  public:
+    virtual int Calculate() override
+    {
+        // 算法3
+    }
+};
+
+class CalculateClass
+{
+  private:
+    CalculateCls *_cal;
+
+  public:
+    void setCalculateCls(CalculateCls *cls)
+    {
+        this->_cal = cls;
+    }
+    // 使用策略模式之后,calculateFunc方法无需根据类型修改,类型增加删除也不会影响该方法,需要修改只是设置的CalculateCls对象
+    void calculateFunc() 
+    {
+        cls.Calculate();
+    }
+}
+```
+
+> 警惕if~else和 switch的使用,有些情况使用策略模式来替换是更好的选择
+
+1. 策略模式及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间切换
+2. 策略模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合
+3. 如果策略对象没有实例变化,那么各个上下文可以共享同一个策略对象,从而节省对象开销
+
+### 观察者模式
+
+需要为某些对象建立一种**“通知依赖关系”**——一个对象的状态发生改变,所有的依赖对象都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好的抵御变化
+
+```cpp
+
+/**
+ * 常规写法 
+ */
+class ReadFile
+{
+  private:
+    PrograssBar *_progreess;
+
+  public:
+    ReadFile(const std::string &filename)
+    {
+        // ....
+    }
+    void setProgress(ProgressBar *progress)
+    {
+        this->_progreess = progress;
+    }
+    void read()
+    {
+        bool readFinish = false;
+        while (readFinish)
+        {
+            // ... 读取处理
+            _progreess->updateProgress();
+        }
+    }
+};
+
+```
+
+> 该写法不符合依赖倒置原则,这里依赖的具体的实现细节,如果后面不想用progress(进度条)而是想要文本框,岂不是又需要修改代码吗
+
+```cpp
+/**
+ * 观察者模式
+ */
+class Observer // 基类
+{
+  public:
+    virtual void update(int val) = 0;
+    virtual void ~Observer() {}
+};
+
+class PrograssBar : public Forum, public Observer{
+	// .... 进度条处理
+	// Forum是普通的显示基类
+	virtual void update(int val) override{
+		// 更新进度条操作
+	}
+};
+class Text: public Forum, public Observer{
+	// .... 文本处理
+	// Forum是普通的显示基类
+	virtual void update(int val) override{
+		// 更新文本内容
+	}
+};
+class ReadFile 
+{
+  private:
+    Observer *_observer ;
+
+  public:
+    ReadFile(const std::string &filename)
+    {
+        // ....
+    }
+    void setProgress(Observer *progress)
+    {
+        this->_progreess = progress;
+    }
+    void read()
+    {
+        bool readFinish = false;
+        while (readFinish)
+        {
+            // ... 读取处理
+            _progreess->update();
+        }
+    }
+};
+```
+
+1. 使用面向对象的抽象,观察者模式使得我们可以**独立**改变目标与观察者,从而使二者之间的依赖关系松耦合
+2. 目标发送通知时,无需指定观察者,通知会自动传播
+3. 观察者自己决定是否需要订阅通知,目标对象对此一无所知
+4. 观察者模式是基于时间的UI框架中非常常用的设计模式,也是MVC模式的重要组成部分
+
+### 装饰模式
+
+某些情况下过度地使用继承来扩展对象的功能,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且伴随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的碰撞
+
+```cpp
+/**
+ * 常规写法 
+ */
+ // 接口
+ class MyStream{
+ 	virtual char Read(int number) = 0;
+ 	virtual void Seek(int pos) = 0;
+ 	virtual void Write(char data) = 0;
+
+	virtual void ~MyStream() {};
+ };
+// 文件流
+class FileStream : public MyStream{
+public:
+	virtual char Read(int number){
+	}
+	virtual void Seek(int pos){
+	}
+	virtual void Write(char data){
+	}
+	virtual FileStream (){}
+};
+// 网络流
+class NetworkStream : public MyStream{
+	// ....
+};
+// 加密文件流
+class CryptoFileStream : public FileStream{
+public:
+	virtual char Read(int number){
+		// 加密操作
+		FileStream::Read(number);
+		// 加密操作
+	}
+	virtual void Seek(int pos){
+		// 加密操作
+		FileStream::Seek(pos);
+		// 加密操作
+	}
+	virtual void Write(char data){
+		// 加密操作
+		FileStream::Write(data);
+		// 加密操作
+	}
+};
+// 网络流加密
+class CryptoNetworkStream : public NetworkStream {
+	// ...
+};
+// 文件流缓冲
+class BufferedFileStream : public FileStream{
+	// ...
+};
+// 网络流流缓冲
+class BufferedNetworkStream : public NetworkStream{
+	// ...
+};
+```
+
+> `CryptoNetworkStream `和`CryptoFileStream `的加密操作几乎相同,`BufferedFileStream `和`BufferedNetworkStream`缓存操作几乎相同,除了内部调用`read`、`write`和`Seek`调用的基类函数不同,可见这里存在明显的代码冗余
+
+```cpp
+/**
+ * 装饰模式
+ */
+ // 接口
+ class MyStream{
+ 	virtual char Read(int number) = 0;
+ 	virtual void Seek(int pos) = 0;
+ 	virtual void Write(char data) = 0;
+
+	virtual void ~MyStream() {};
+ };
+// 文件流
+class FileStream : public MyStream{
+public:
+	virtual char Read(int number){
+	}
+	virtual void Seek(int pos){
+	}
+	virtual void Write(char data){
+	}
+	virtual FileStream (){}
+};
+// 网络流
+class NetworkStream : public MyStream{
+	// ....
+};
+// 加密流
+class CryptoStream : public MyStream{
+	MyStream* stream;
+public:
+	CryptoStream (MyStream* str) : stream(str) {}
+	virtual char Read(int number){
+		// 加密操作
+		stream->Read(number);
+		// 加密操作
+	}
+	virtual void Seek(int pos){
+		// 加密操作
+		stream->Seek(pos);
+		// 加密操作
+	}
+	virtual void Write(char data){
+		// 加密操作
+		stream->Write(data);
+		// 加密操作
+	}
+};
+// 缓冲流
+class BufferedStream : public MyStream{
+	MyStream* stream;
+public:
+	BufferedStream(MyStream* str) : stream(str) {}
+	virtual char Read(int number){
+		// 缓冲操作
+		stream->Read(number);
+		// 缓冲操作
+	}
+	virtual void Seek(int pos){
+		// 缓冲操作
+		stream->Seek(pos);
+		// 缓冲操作
+	}
+	virtual void Write(char data){
+		// 缓冲操作
+		stream->Write(data);
+		// 缓冲操作
+	}
+};
+```
+
+1. 通过采用组合而非继承的手法,装饰模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能
+2. 装饰模式的类在接口上表现了继承关系,又表现了组合关系
+3. 装饰模式的目的并非解决“多子类衍生的多继承”问题,装饰模式应用的要点在于解决**“主体类在多个方向上的扩展功能”**
+
+### 桥模式
+
+```cpp
+class Messager{
+public:
+	virtual void SendMessage() = 0;
+	virtual void WriteText() = 0;
+	virtual ~Messager() {}
+};
+
+// 平台实现
+class PcMessagerBase : public Messager{
+	virtual void SendMessage() {
+	}
+	virtual void WriteText() {
+	}
+};
+// 移动
+class MobileMessagerBase : public Messager{
+	virtual void SendMessage() {
+	}
+	virtual void WriteText() {
+	}
+};
+
+```
+
+### 工厂模式
+
+在软件系统中,经常面临创建对象的工作,有需求的变化,需要创建的对象的具体类型经常变化
+
+```cpp
+// 分割器
+class Spliter{ };
+class BinarySpliter : public Spliter{};	// 二进制分割器
+class PictureSpliter : public Spliter{};// 图片分割器
+class TxtSpliter : public Spliter{};//文本分割器
+
+class MainForum : public Forum{
+public:
+	void Click(const std::string &file){
+		Spliter* spliter = new BinarySpliter();	
+		// ...
+	}
+};
+
+```
+
+> 这里使用的分割器依赖了具体类
+
+```cpp
+class Spliter{ };
+class BinarySpliter : public Spliter{};	// 二进制分割器
+class PictureSpliter : public Spliter{};// 图片分割器
+class TxtSpliter : public Spliter{};//文本分割器
+
+class SpliterFactory{
+public:
+	Spliter* CreateSpliter() = 0;
+	virtual ~SpliterFactory(){}
+};
+class BinarySpliterFactory{
+public:
+	Spliter* CreateSpliter() {
+		return new BinarySpliter();
+	}
+};
+class PictureSpliterFactory{
+public:
+	Spliter* CreateSpliter() {
+		return new PictureSpliter ();
+	}
+};
+class TxtSpliterFactory{
+public:
+	Spliter* CreateSpliter() {
+		return new TxtSpliter ();
+	}
+};
+
+class MainForum : public Forum{
+private:
+	SpliterFactory* _spliterFactory;
+public:
+	void setSpliteerFactory(SpliterFactory* spliterFactory){
+		_spliterFactory = spliterFactory;
+	}
+	void Click(const std::string &file){
+		Spliter* spliter = _spliterFactory->CreateSpliter();	
+		// ...
+	}
+};
+```
+
+> 把动态的(具体是哪个工厂也需要明确指出)部分赶出了该类的设计
+
+定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使得一个类的实例化延迟到子类
+
+1. 工厂模式用于隔离类对象的使用者和具体类之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱
+2. 工厂模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展的策略
+3. 工厂模式解决单个对象的需求变化,缺点在于要求创建方法/参数相同
+
+### 抽象工厂模式
+
+在软件系统中,经常面临这“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作
+
+```cpp
+class DBConnection {};
+class DBCommand();
+class DBDataReader();
+class DBConnectionFactory {};
+class DBCommandFactory {};
+class DBDataReaderFactory {};
+
+class SQLServerConnection:public DBConnection {};
+class SQLServerCommand:public DBCommand {};
+class SQLServerDataReader:public DBDataReader{};
+class SQLServerConnectionFactory:public DBConnectionFactory  {};
+class SQLServerCommandFactory:public DBCommandFactory  {};
+class SQLServerDataReaderFactory:public DBDataReaderFactory {};
+
+
+class OracleConnection : public DBConnection {};
+class OracleCommand : public DBCommand{};
+class OracleDataReader : public DBDataReader {};
+class OracleConnectionFactory :public DBConnectionFactory  {};
+class OracleCommandFactory :public DBCommandFactory  {};
+class OracleDataReaderFactory :public DBDataReaderFactory {};
+
+
+class MySqlConnection : public DBConnection {};
+class MySqlCommand : public DBCommand{};
+class MySqlDataReader : public DBDataReader{};
+class MySqlConnectionFactory :public DBConnectionFactory  {};
+class MySqlCommandFactory :public DBCommandFactory  {};
+class MySqlDataReaderFactory :public DBDataReaderFactory {};
+
+class Dao{
+private:
+	DBConnectionFactory * _connection; 
+	DBCommandFactory * _command;
+	DBDataReaderFactory * _dataReader;
+public:
+	std::vector<Student> getAllStudent(){
+		
+		// ...
+	}
+};
+```
+
+> 存在一个问题是:Connection、Command、DataReder必须是同一系列的,不同系列肯定出错
+
+```cpp
+
+class DBConnection {};
+class DBCommand();
+class DBDataReader();
+class DBFactory{
+	virtual DBConnection* CreaeConnection() = 0;
+	virtual DBCommand* CreaeCommand() = 0;
+	virtual DBDataReader* CreaeDataFactory() = 0;
+};
+
+class Dao{
+private:
+	DBFactory * _factory; 
+public:
+	std::vector<Student> getAllStudent(){
+		
+		// ...
+	}
+};
+```
+
+> **提供一个接口,让该结构负责创建一系列相关或者互相依赖的对象,无需执行他们具体的类**
+
+### 原型模式
+
+在软件系统中,经常面临着某些结构复杂的对象的创建工作,由于需求的变化,这些对象经常面临这剧烈的变化,但是他们却拥有比较稳定一直的接口
+
+使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象
+
+```cpp
+/**
+ * Prototype 原型模式
+ * 由于不知道未来Image的子类的名称,所以Image使用一个_prototypes来记录子类的原型
+ * 原型需要实现自己的Clone()函数,方便Image通过Type来管理和创建
+ * 原型需要实现自己的returnType()函数,方便Image来获得Type做一些判断
+ * 原型需要一个static的自己并且在默认构造函数中将自己添加到Image的管理中
+ * 
+ * ——————————————————————————————————————————————————————
+ * 
+ * 通过上面的操作,可以用Image创建出其子类,不管这个子类在当时知不知道
+ * 原型模式的重点是Clone和基类的管理,子类的构造函数在protected和private是因为这些子类不准备自己创建,而是通过Image来创建
+ **/
+
+enum imageType
+{
+    LAST,
+    SPOT
+};
+
+class Image
+{
+  public:
+    virtual void draw() = 0;
+    static Image *findAndClone(imageType type)
+    {
+        for (int i = 0; i < _nextSlot; i++)
+        {
+            if (_prototypes[i]->returnType() == type)
+            {
+                return _prototypes[i]->Clone();
+            }
+        }
+    }
+
+  protected:
+    virtual imageType returnType() = 0;
+    virtual Image *Clone() = 0;
+
+    static void addPrototype(Image *image)
+    {
+        _prototypes[_nextSlot++] = image;
+    }
+
+  private:
+    static Image *_prototypes[10];
+    static int _nextSlot;
+};
+
+Image *Image::_prototypes[];
+int Image::_nextSlot;
+
+class NewImageType : public Image
+{
+  public:
+    Image *Clone() override
+    {
+        return new NewImageType(1);
+    }
+
+    imageType returnType() override
+    {
+        return imageType::LAST;
+    }
+
+    void draw() override
+    {
+        std::cout << "NewImageType" << std::endl;
+    }
+
+  protected:
+    NewImageType(int _dummy) // 为了跟默认构造函数做区别,所以加了个参数,因为默认构造函数是注册用的
+    {
+        _id = _count++;
+    }
+
+  private:
+    static NewImageType _newImageType; // 创建一个static的自己,调用下面的默认构造,将自己添加到Image的管理中
+    NewImageType()
+    {
+        addPrototype(this); // 将自己添加到Image的管理中
+    }
+    int _id;
+    static int _count;
+};
+
+NewImageType NewImageType::_newImageType;
+int NewImageType::_count = 1;
+```
+
+### 构建器
+
+在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临这剧烈的变化,但是将他们组合在一起的算法却相对稳定
+
+构建器:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)
+
+```cpp
+class House{
+public:
+	void init(){
+		this->BuilderPart1();
+		// ...其他操作
+		this->BuilderPart2();
+		// ...其他操作
+		this->BuilderPart3();
+		// ...其他操作
+		this->BuilderPart4();
+		// ...其他操作
+	}
+	virtual ~House(){}
+protected:
+	virtual void BuilderPart1() = 0;
+	virtual void BuilderPart2() = 0;
+	virtual void BuilderPart3() = 0;
+	virtual void BuilderPart4() = 0;
+};
+
+class StoneHouse : public House{
+	virtual void BuilderPart1() {}
+	virtual void BuilderPart2() {}
+	virtual void BuilderPart3() {}
+	virtual void BuilderPart4() {}
+};
+
+int main()
+{
+	StoneHouse stoneHouse = new StoneHouse ();
+	stoneHouse->init();
+	return 0;
+}
+```
+
+> 这个还能继续优化,将init部分拆出来等
+
+1. 分步骤构建一个复杂的对象,分步骤是一个稳定算法,复杂对象的各个部分经常变
+2. 比那花点在哪,就封装哪里。构建器主要在于应对复杂对象各个部分的频繁需求变动,缺点是难以应对分步骤构建算法的变动
+3. 需要注意不同语言中构造器内部调用虚函数的区别
+
+### 单例模式
+
+在软件系统中,经常有一些特殊的类,必须保证他们在系统中只存在一个实例,才能确保他们的逻辑正确性以及良好的效率
+
+```cpp
+/**
+ * 单例模式
+ * 
+ */
+
+class Singleton
+{
+  private:
+	Singleton();					   // 构造需要是私有 防止外部创建
+	Singleton(const Singleton &other); // 拷贝构造需要是私有 防止外部创建
+
+  public:
+	static Singleton *getInstance() // 多线程下不安全,可能多次执行new操作
+	{
+		if (m_instance == nullptr)
+		{
+			m_instance = new Singleton();
+		}
+		return m_instance;
+	}
+	static Singleton *getInstance() //加锁 保证多线程安全 但是代价较高每次调用getInstance都会加锁
+	{
+		Lock lock;
+		if (m_instance == nullptr)
+		{
+			m_instance = new Singleton();
+		}
+		return m_instance;
+	}
+	static Singleton *getInstance() //双检查锁,但由于内存读写reorder不安全
+	{
+		if (m_instance == nullptr)
+		{
+			Lock lock;
+			if (m_instance == nullptr)
+			{
+				m_instance = new Singleton();
+			}
+		}
+		return m_instance;
+	}
+	static Singleton *m_instance;
+};
+
+Singleton *Singleton::m_instance = nullptr;
+
+class Single
+{
+  public:
+	static Single &GetInstance();
+
+  private:
+	Single();
+	~Single();
+	Single(const Single &signal);
+	const Single &operator=(const Single &signal);
+};
+
+Single &Single::GetInstance()
+{
+	static Single signal;
+	return signal;
+}
+```
+
+> 单例模式需要注意线程安全问题   
+> 注意构造、拷贝构造的位置,防止外部有人主动new出对象  
+
+### 享元模式
+
+在软件系统采用纯粹面向对象方案的问题在于大量细粒度的对象会很快充斥再系统中,从而带来很高的运行时代价——主要指内存需求方面的代价  
+
+运用共享技术有效地支持大量细粒度的对象   
+
+```cpp
+ 
+class Font
+{
+  private:
+	std::string key; // 字体的key,通过key获得相关的资源
+
+  public:
+	Font(const string &key)
+	{
+	}
+};
+
+class FontFactory
+{
+  private:
+	std::map<std::string, Font *> fontPool;
+
+  public:
+	Font *GetFontData(const std::string &key)
+	{
+		map<std::string, Font *>::iterator item = fontPool.find(key);
+		if (item != fontPool.end())
+		{
+			return fontPool[key];
+		}
+		else
+		{
+			Font *font = new Font(key);
+			fontPool[key] = font;
+			return font;
+		}
+	}
+}
+```
+
+1. 面向对象很好的解决了抽象性的问题,但是作为运行计算机中的实体程序,我们需要考虑对象的代价问题。享元模式主要解决面向对象的代价问题,一般触及面向对象的抽象性问题
+2. 享元模式采用对象共享的的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理
+3. 对象数量的多少需要根据具体应用情况进行评估
+
+### 门面模式
+
+![请添加图片描述](https://img-blog.csdnimg.cn/64fb197170af4f05bb3d0b5c13189468.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Zi_6Iy25a6255qE5bq45Yy7,size_20,color_FFFFFF,t_70,g_se,x_16)
+新增一层接口,隐藏内部模块的对象接口,同时也可以方便外部调用  
+
+1. 从客户程序的角度来看,门面模式简化了整个组件系统的接口,对于组件内部与外部客户程序来说,达到了一种解耦的效果,内部子系统的任何变化都不会影响到门面模式接口的变化
+2. 门面模式更注重从架构的层次去看整个系统,而不是单个类的层次
+3. 门面模式并非一个集装箱,可以任意的放入任意多个对象。门面模式内部组件必须是互相耦合关系比较大的一系列组件
+
+### 代理模式
+
+在面向对象系统中,有些独享由于某些原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦
+
+
+![请添加图片描述](https://img-blog.csdnimg.cn/67fd3bbcf3e543708b7691b64072d5c2.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Zi_6Iy25a6255qE5bq45Yy7,size_20,color_FFFFFF,t_70,g_se,x_16)
+
+1. 增加一层间接层,是软件系统中对许多复杂问题的一种常见解决方法
+2. 具体的代理模式设计的实现方法、实现粒度差别很大,有可能对单个对象做细粒度的控制,如copy-on-write技术,有可能对组件模块提供抽象代理曾,在架构层次对对象做proxy
+3. 代理模式不一定要保持接口完整的一致性,只要能实现间接控制,有时候损失一些透明性是可以接受的
+
+### 适配器模式
+
+在软件系统中,由于应用环境的变化,常常需要将一些显存的对象放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的
+
+适配器模式是将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
+
+![请添加图片描述](https://img-blog.csdnimg.cn/2f52922024d5437c8bd4aad6a4af00ea.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Zi_6Iy25a6255qE5bq45Yy7,size_20,color_FFFFFF,t_70,g_se,x_16)
+
+### 中介者模式
+
+在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化
+
+用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各个对象不需要显式的相互引用(编译时依赖->运行时依赖),从而使其耦合松散(管理变化),而且可以独立地改变它们之间的交互  
+
+### 状态模式
+
+在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?状态模式为这问题提供了解决方案
+
+某些对象的状态改变,他的行为也随之发生变化,比如文档处于只读状态
+状态模式:允许一个对象在其内部状态改变时改变它的行为,从而使对象看起来似乎修改了其行为
+
+```cpp
+enum NetworkState {
+	Network_Open,
+	Network_Close,
+	Network_Connect,
+};
+
+class NetworkProcessor
+{
+	NetworkState _state;
+
+  public:
+	void Operatrion1()
+	{
+		switch (_state)
+		{
+		case NetworkState::Network_Open:
+			// 一些操作
+			_state = NetworkState::Close;
+			break;
+		case NetworkState::Network_Close:
+			// 一些操作
+			_state = NetworkState::Network_Connect;
+			break;
+		case NetworkState::Network_Connect:
+			// 一些操作
+			_state = NetworkState::Network_Open;
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+```
+
+> 跟以前策略模式的反例一样 违反了开闭原则
+
+```cpp
+
+/**
+ * 状态模式
+ * 
+ */
+
+enum NetworkState {
+	Network_Open,
+	Network_Close,
+	Network_Connect,
+};
+
+class NetworkStateBase
+{
+  public:
+	virtual void Operatrion1() = 0;
+	virtual ~NetworkStateBase();
+
+	NetworkStateBase *_state;
+};
+
+class OpenState : public NetworkStateBase
+{
+  public:
+	static OpenState *getInstance()
+	{
+		static OpenState state;
+		return &state;
+	}
+	void Operatrion1()
+	{
+		// Open的一些操作
+		this->_state = CloseState::getInstance();
+	}
+};
+class CloseState : public NetworkStateBase
+{
+  public:
+	static CloseState *getInstance()
+	{
+		static CloseState state;
+		return &state;
+	}
+	void Operatrion1()
+	{
+		// Close的一些操作
+		this->_state = ConnectState::getInstance();
+	}
+};
+class ConnectState : public NetworkStateBase
+{
+  public:
+	static ConnectState *getInstance()
+	{
+		static ConnectState state;
+		return &state;
+	}
+	void Operatrion1()
+	{
+		// Connect的一些操作
+		this->_state = OpenState::getInstance();
+	}
+}
+
+class NetworkProcessor
+{
+  private:
+	NetworkStateBase *_netstate;
+
+  public:
+	NetworkProcessor(NetworkStateBase *pState)
+	{
+		_netstate = pState;
+	}
+	void Operatrion1()
+	{
+		_netstate->Operatrion1();
+		_netstate = _netstate->_state;
+	}
+}
+```
+
+> 这里每个状态一个单例模式,是因为很多情况下单例就行了,具体情况具体分析  
+> 这个每个状态都单独写了一个类,保证NetworkProcessor的开闭原则  
+
+1. 状态模式将所有于一个特定状态相关的行为都放在一个状态的子类对象中,再度向状态切换时,切换相应的对象;但同时维持状态的接口,这样实现了具体操作与状态转换之间的解耦
+2. 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——要么彻底转换过来,要么不转换
+3. 如果状态对象没有实力变量,那么各个上下文可以共享同一个状态对象,从而节省对象的开销
+
+### 备忘录

+ 691 - 0
cpp/面向对象.md

@@ -0,0 +1,691 @@
+# 基础知识
+
+## 不带指针的类
+
+```cpp
+#ifndef __TESTCLASS_NOPOINT__
+#define __TESTCLASS_NOPOINT__
+
+#include <iostream>
+using namespace std;
+
+class TestClass // class head
+// class body
+{
+public:                                                 // 访问级别 public(大部分函数)、private(一般数据放在这里)、proteted
+    TestClass(int a = 0, int b = 0)                     // 构造函数重载、参数默认值
+    : _a(a), _b(b)                                      // 构造函数的特殊赋值方法(初值列、初始列)这里严格来讲不是赋值而是初始化
+    {
+        // _a = a; _b = b; // 该方法与初值列赋值作用相同,但时间不同,一个在初始化、一个在赋值阶段。在这里赋值时间点会晚一点
+    }        
+
+    // 一般构造函数在public作用域,但也可以放在private中,经典案例就是单例模式
+    // TestClass(): _a(0), _b(0) {}   该构造函数与上面的构造函数冲突了
+
+    void setA(const int &a) {this->_a = a;}             // 尽可能多的使用引用传参,并搭配const使用,节省数据传递的消耗
+    void setB(const int &b) {this->_b = b;}             // 引用传递一般四个字节,一般对象都大于四个字节所以大部分情况下传递引用可以节省时间
+
+    inline int getA() const {return this->_a;}          // inline 内联函数 最终由编译器决定是否可以构成内联函数
+    int getB() const {return this->_b;}                 // 记得在必要的时候加上const,只要没有修改值的函数建议都加上const;否则const对象无法调用非const函数
+
+    /**
+     * 友元
+    **/
+
+    friend ostream& operator<<(ostream &os, const TestClass &x) // 友元
+    {
+        os << '(' << x.getA() << " " << x.getB() << ')';
+        return os;
+    }
+
+    // friend void set_show(int x, A &a);               // 友元函数
+    // friend class FriendClass;                        // 友元类
+    // friend void FriendClass::set_show(int x, A &a);  // 友元成员函数
+
+    // 两个相同类的Object 互为友元,所以该函数中可以直接访问temp的private作用域数据
+    void CloneFromOtherA(const TestClass &temp){
+        this->_a = temp._a;
+        this->_b = temp._b;
+    }
+
+    /**
+     * 操作符重载 
+    */
+
+    inline TestClass& operator += (const TestClass &x)
+    {
+        return _doapl(this, x);
+    }
+
+protected:
+    friend TestClass& _doapl(TestClass* ths, const TestClass& r);    // 返回一个相加的对象
+
+private:
+    int _a;
+    int _b;
+};
+
+/**
+ * 定义在类外的操作符重载 
+ * https://www.runoob.com/cplusplus/cpp-overloading.html
+ * 下面为不可重载操作符
+    .:成员访问运算符
+    .*, ->*:成员指针访问运算符
+    :::域运算符
+    sizeof:长度运算符
+    ?::条件运算符
+    #: 预处理符号
+**/
+TestClass& _doapl(TestClass* ths, const TestClass& r) 
+{
+    ths->_a += r._a;
+    ths->_b += r._b;
+    return *ths;            // 该对象不是在函数体中创建的临时对象,所以可以返回引用回去    
+}
+
+inline TestClass operator +(const TestClass &x, const TestClass &y)     // 使用环境为 A + B
+{
+    // 这里的返回值是函数体中的临时对象,所以返回值不能返回引用
+    return TestClass(x.getA() + y.getA(), x.getB() + y.getB());
+}
+
+inline TestClass operator +(const TestClass &x, int y)                  // 使用环境为 A + 1
+{
+    return TestClass(x.getA() + y, x.getB());
+}
+
+inline TestClass operator +(int y, const TestClass &x)                  // 使用环境为 1 + A
+{
+    return TestClass(x.getA() + y, x.getB());
+}
+
+inline TestClass operator + (const TestClass &x)                        // 正号 正100
+{
+    return TestClass(x.getA(), x.getB());
+}
+
+inline TestClass operator - (const TestClass &x)                        // 负号 负100
+{
+    return TestClass(-x.getA(), -x.getB());
+}
+
+inline bool operator == (const TestClass &x, const TestClass &y)        // A == B
+{
+    return x.getA() == y.getA() && x.getB() == y.getB();
+}
+
+inline bool operator == (const TestClass &x, const int &y)              // A == 1
+{
+    return x.getA() == y && x.getB() == 0;
+}
+
+inline bool operator == (const int &x, const TestClass &y)              // 1 == A
+{
+    return x == y.getA() && y.getB() == 0;
+}
+
+#endif
+```
+
+## 带指针的类
+
+```cpp
+
+#ifndef __TESTCLASS_POINTER__
+#define __TESTCLASS_POINTER__
+
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+using namespace std;
+/**
+ A = B 拷贝赋值           编译器自动生成
+ A = String(B) 拷贝构造   编译器自动生成 
+ 如果Class不存在指针,编译器自动生成问题不大
+ 如果Class存在指针,则拷贝为浅拷贝,即两个Object的成员对象指针指向同一块地址空间
+ 所以如果Class带指针,最好不要使用编译器生成的拷贝构造和拷贝赋值
+*/
+class String
+{
+public:
+    String(const char* cstr = nullptr);
+    String(const String& str);
+    String& operator =(const String &str);
+    ~String();
+    char* get_c_str() const {return m_data;}
+
+private:
+    char* m_data;
+};
+
+String::String(const char* cstr)
+{
+    if (cstr) {
+        m_data = new char[strlen(cstr) + 1];
+        strcpy(m_data, cstr);
+    }
+    else{
+        m_data = new char[1];
+        *m_data = '\0';
+    }
+}
+
+String::~String(){
+    delete[] m_data;
+}
+
+String::String(const String& str)
+{
+    m_data = new char[strlen(str.m_data) + 1];
+    strcpy(m_data, str.m_data); 
+}
+
+String& String::operator=(const String &str)
+{
+    if (this == &str) {
+        // 检测自我赋值 如果不做此处理 后续操作会自己删掉自己的内容 导致问题
+        return *this;
+    }
+    
+    delete[] m_data;
+    m_data = new char[strlen(str.m_data) + 1];
+    strcpy(m_data, str.m_data);
+    return *this;
+}
+
+ostream& operator << (ostream& os, const String &str)
+{
+    os << str.get_c_str();
+    return os;
+}
+
+/**
+ * 建议理解new的底层操作:1.申请内存 2. 类型转换 3. 构造函数
+    void *mem = operator new (sizeof(String)); // 底层调用malloc()
+    pc = static_cast<String*>(mem);
+    pc->String::String("hello");
+ * delete的底层操作
+    String::~String(pc);   // 调用析构函数
+    operator delete(pc);   // 底层调用free()释放内存 
+**/
+
+#endif
+
+int main()
+{
+    String s1("hello");
+    std::cout << s1 << endl;
+    system("pause");
+    return 0;
+}
+```
+
+```cpp
+delete[] ptr;
+```
+
+> delete数组时需要使用delete[]  
+> 如果有中括号[],系统知道要删除多个  
+> 如果没有中括号,系统不知道要删除多个,因此数组中只有第一个执行了析构,其他的都没有析构  
+> 如果成员对象中没有new出来的指针,其实delete也可以,如果有就会内存泄漏
+
+![请添加图片描述](https://img-blog.csdnimg.cn/cfd4d4bd62a0465fbfd3f594f451d093.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6Zi_6Iy25a6255qE5bq45Yy7,size_20,color_FFFFFF,t_70,g_se,x_16)
+
+## sataic
+
+```cpp
+#include <iostream>
+using namespace std;
+
+#ifndef __STATIC_STU__
+#define __STATIC_STU__
+
+class StaticStuClass
+{
+  public:
+    static int GetA(const StaticStuClass &x) { return x.m_a; }
+    static int GetB(const StaticStuClass &x) { return x.m_b; }
+
+    static void SetC(const int &val) { s_C = val; } // static函数只能处理static的成员对象,普通数据和普通函数不能调用
+    static int GetC() { return s_C; }
+
+    static StaticStuClass &getInstance()
+    {
+        static StaticStuClass self;
+        return self;
+    }
+
+  public:
+    void setup()
+    {
+    }
+
+    StaticStuClass(int _a = 2, int _b = 2) : m_a(_a), m_b(_b)
+    {
+    }
+    ~StaticStuClass()
+    {
+    }
+
+    int getA() const { return this->m_a; }
+    int getB() const { return this->m_b; }
+
+  private:
+    int m_a, m_b;
+
+    static int s_C; // 不管存在多少个StaticStuClass对象,s_C数据只存在一份,m_a、m_b一个对象就有一个
+};
+
+int StaticStuClass::s_C = 0; // static的成员对象记得初始化,否则容易报错
+
+#endif
+
+int main()
+{
+    StaticStuClass cls(1, 2);
+    StaticStuClass::SetC(1);
+    StaticStuClass::getInstance().setup();
+    std::cout << cls.getA() << " " << StaticStuClass::GetA(cls) << std::endl;
+    system("pause");
+    return 0;
+}
+
+```
+
+## 继承—组合—委托
+
+```cpp
+
+#ifndef __Inheritance_Composition_Delegation__
+#define __Inheritance_Composition_Delegation__
+
+#include <iostream>
+#include <queue>
+#include <deque>
+
+using namespace std;
+
+/**
+ * Composition 复合 
+ * MyQueue 拥有 deque 作为组件,MyQueue因此也拥有deque的功能,所以MyQueue的很多功能可以基于deque改装一下即可
+ * 构造由内到外:先调用Component的默认构造函数,然后再执行自己的
+ * 析构由外而内:首先执行自己析构函数,然后再调用Component的析构函数
+*/
+template <typename T>
+class MyQueue
+{
+  public:
+	bool empty() const { return c.empty(); }
+	size_type 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(); }
+
+  protected:
+	std::deque<T> c;
+};
+
+/**
+ * delegation 委托 Composition By Reference
+ * MyString拥有的是一个指针,而不是Component的实体
+ * 一种用法:一个StringRep可以同时被多个MyString拥有(可以搭配copy on write写时复制,防止某个MyString对StringRep的修改影响了其他使用该StringRep的MyString)
+ * 一种用法:单纯修改MyString中StringRep指针指向的对象就可以修改MyString的功能,而不用修改MyString对外暴露的接口
+*/
+
+class StringRep;
+
+class MyString
+{
+  public:
+	MyString();
+	MyString(const char *s);
+	MyString(const MyString &s);
+	MyString &operation = (const MyString &s);
+	~MyString();
+
+  private:
+	StringRep *rep;
+};
+
+class StringRep
+{
+	freind class MyString;
+	StringRep(const char *str);
+	~StringRep();
+	int count;
+	char *rep;
+};
+
+/**
+ * Inheritance 继承 表示 is-a 的关系
+ * 继承分为public、private、proteted三种,对应访问父类的作用域不同
+ * 构造由内而外:首先调用基类的构造函数,然后才执行子类的构造
+ * 析构由外到内:首先调用子类的析构函数,然后才执行基类的析构
+ * 基类的析构函数必须是virtual,否则容易出现内存泄漏
+ * non-virtual函数:不希望派生类override的函数
+ * virtual函数:希望派生类override的函数,并且给出了默认定义
+ * pure virtual 函数:希望派生类override的函数,并且没有给出默认定义
+ * 
+ * ————————————————————————————————————————————————————————————————
+ * 
+ * 一种用法:搭建框架,由基类定好一个基本框架,并将变更的地方设定为虚函数/纯虚函数,由使用者继承并实现
+**/
+
+class Animal // 基类
+{
+  private:
+  public:
+	Animal();
+	Animal(int a, int b);
+	virtual ~Animal();
+	void dead();			// 普通函数
+	virtual void run();		// 虚函数
+	virtual void eat() = 0; // 纯虚函数
+};
+
+class Cat : public Animal // 子类
+{
+  private:
+  public:
+	Cat();							  // 调用基类默认的构造函数
+	Car(int a, int b) : Animal(a, b); // 调用指定的构造函数
+	virtual ~Cat();
+};
+
+#endif
+```
+
+## 转换函数
+
+```cpp
+#include <iostream>
+#include <cstring>
+using namespace std;
+
+#ifndef __conversion_function__
+#define __conversion_function__
+
+/**
+ * 转换函数
+ * 一般来说转换数据类型到时候不会修改数据内容,所以需要一般要加上const
+ * 告诉编译器在需要将该类型转换成指定类型的时候使用该方法转换
+ **/
+
+class Fraction
+{
+  public:
+    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
+    // 转换函数,告诉编译器在需要将Fraction转换成double的时候使用该方法转换
+    // 因为是转换函数,其内部的值不能发生修改,所以需要const
+    operator double() const
+    {
+        return (double)(m_numerator * 1.0 / m_denominator);
+    }
+    operator string() const
+    {
+        char numerator[10];
+        char denominator[10];
+        memset(numerator, 0, sizeof(numerator));
+        memset(denominator, 0, sizeof(denominator));
+        itoa(m_numerator, numerator, 10);
+        itoa(m_denominator, denominator, 10);
+        return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
+    }
+
+  private:
+    int m_denominator; // 分母
+    int m_numerator;   // 分子
+};
+
+#endif
+
+int main()
+{
+    Fraction f(3, 5);
+    // 编译器优先寻找operator (int, Fraction)的操作符重载,没有发现就再查找Fraction能否转成double
+    std::cout << 1 + f << std::endl;
+    std::cout << (std::string)f << std::endl;
+    system("pause");
+    return 0;
+}
+```
+
+## explicit
+
+```cpp
+
+/**
+ * non-explicit-one-argument 只要一个实参就够了的构造函数
+ * 下面例子中:没有写oprator * (Fraction, int)的操作符重载也不会报错,因为编译器将4通过一个参数的构造变成Fraction(4, 1)类型
+ * 当构造函数前加上explicit关键字时,编译器不会进行隐式构造函数将4变成Fraction(4, 1)
+ * 
+ * ——————————————————————————————————————————————————————————————————————————————
+ * 如果构造函数不存在explicit,且有operator double()转换函数,编译器报错,因为存在二义性即两种方式都可以行得通且没有优先级这么一说
+ * 如果构造函数存在explicit,且有operator double()转换函数,编译器不报错,因为只有一条路可走,将Fraction转换成double计算
+ * 如果构造函数不存在explicit,且没有operator double()转换函数,编译器不报错,因为只有一条路可走,将4转换成Fraction计算
+ **/
+
+class Fraction
+{
+  public:
+    // 这里构造函数只需要一个参数其实也够
+    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
+    //explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
+
+    Fraction operator*(const Fraction &f)
+    {
+        return Fraction(f.m_numerator * this->m_numerator, f.m_denominator * this->m_denominator);
+    }
+    std::string getString()
+    {
+        char numerator[10];
+        char denominator[10];
+        memset(numerator, 0, sizeof(numerator));
+        memset(denominator, 0, sizeof(denominator));
+        itoa(m_numerator, numerator, 10);
+        itoa(m_denominator, denominator, 10);
+        return std::string(std::string(numerator) + std::string("/") + std::string(denominator));
+    }
+
+  private:
+    int m_denominator; // 分母
+    int m_numerator;   // 分子
+};
+
+int main()
+{
+    Fraction f(3, 5);
+    Fraction f2 = f * 4;
+    std::cout << f2.getString() << std::endl;
+    system("pause");
+    return 0;
+}
+```
+
+> explicit告诉编译器,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
+
+## 特化模板和模板偏特化
+
+```cpp
+
+#include <iostream>
+#include <vector>
+using namespace std;
+
+/**
+ * specialization 模板特化
+ * 代码来源:https://www.cnblogs.com/dongzhiquan/p/7726379.html
+ */
+
+template <class T>
+class Compare
+{
+  public:
+    static bool IsEqual(const T &lh, const T &rh)
+    {
+        std::cout << "normal compare" << std::endl;
+        return lh == rh;
+    }
+};
+
+template <>
+class Compare<double>
+{
+  public:
+    static bool IsEqual(const double &lh, const double &rh)
+    {
+        std::cout << "float compare" << std::endl;
+        return abs(lh - rh) < 10e-6;
+    }
+};
+
+template <>
+class Compare<float>
+{
+  public:
+    static bool IsEqual(const float &lh, const float &rh)
+    {
+        std::cout << "double compare" << std::endl;
+        return abs(lh - rh) < 10e-3;
+    }
+};
+
+/**
+ * partial specialization 模板偏特化
+ **/
+
+// 范围上不同的偏特化
+// 普通类型和指针类型的特化
+
+template <typename T>
+class C
+{
+  public:
+    C()
+    {
+        std::cout << "normal special" << std::endl;
+    }
+};
+
+template <typename U>
+class C<U *>
+{
+  public:
+    C()
+    {
+        std::cout << "pointer special" << std::endl;
+    }
+};
+
+// ----------------------------------
+// 数量上不同的偏特化,比如这里我觉得vec对bool需要特殊处理,所以做了对bool偏特化
+
+template <typename T, typename Alloc>
+class MyVec
+{
+  public:
+    MyVec()
+    {
+        std::cout << "normal vec" << std::endl;
+    }
+};
+
+template <typename Alloc>
+class MyVec<bool, Alloc>
+{
+  public:
+    MyVec()
+    {
+        std::cout << "bool vec" << std::endl;
+    }
+};
+
+int main()
+{
+    Compare<double>::IsEqual(1.0, 1.0);
+    Compare<float>::IsEqual(1.0f, 1.0f);
+    Compare<int>::IsEqual(1, 1);
+
+    C<string> obj1;
+    C<string *> obj2;
+
+    MyVec<int, int> vec1;
+    MyVec<bool, int> vec2;
+
+    system("pause");
+    return 0;
+}
+```
+
+## 模板模板参数
+
+```cpp
+
+template <typename T,
+          template <typename U>
+          class Container>
+class XCls
+{
+  private:
+    Container<T> c;
+
+  public:
+};
+
+template <typename T>
+using Vst = std::vector<T, allocator<T>>;
+
+int main()
+{
+	// 因为vector需要多个模板参数,平时只给一个是因为有默认模板参数,但是在模板模板参数中只传一个对vector来说会报错,所以用了Vst来代替
+    XCls<std::string, Vst> mylist1;
+    system("pause");
+    return 0;
+}
+```
+
+## 不定参数模板
+
+```cpp
+template <typename T>
+using Vst = std::vector<T, allocator<T>>;
+
+void print()
+{
+}
+
+template <typename T, typename... Types>
+void print(const T &firstArg, const Types &... args)
+{
+    cout << firstArg << endl;
+    print(args...);
+}
+
+int main()
+{
+    print(1, 2, 3, 4, "123");
+
+    system("pause");
+    return 0;
+}
+```
+
+## ranged-base for
+
+```cpp
+std::vector<int> v{1, 2, 3, 4, 5, 6};
+for (auto elem : v)		// 值传递
+{
+    std::cout << elem << endl;
+}
+for (auto &elem : v)	// 引用方式取值 
+{
+    elem *= 2;
+}
+for (auto elem : v)
+{
+    std::cout << elem << endl;
+}
+```

+ 29 - 1
虚幻.md

@@ -1753,5 +1753,33 @@ HUD默认自身是不接受键盘输入事件的,本身HUD就不应直接响
 
 [更多命名规则](https://github.com/uejoy/ue4-style-guide)
 
-## UE的对象
+### 创建Actor
+
+```cpp
+void ADemoGameModeBase::BeginPlay()
+{
+	/**
+	*	代码创建Actor
+	*	获得世界(GetWorld()),调用SpawnActor方法生成对象
+	*	AActor::StaticClass()提供类模板
+	*	FVector::ZeroVector (0,0,0)坐标
+	*	FRotator::ZeroRotator (0,0,0)旋转角
+	/************************************************************************/ 
+	
+	GetWorld()->SpawnActor<AActor>(AActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator);
+}
+```
+
+AActor::StaticClass():合成函数。旨在将操作类作为一个参数进行传递(传递模板)。虚幻中除了使用模板构建对象外,还增加了映射关系,可以将类作为对象构建依据,这可以方便的将类体提供给蓝图使用
+
+> C++本身是编译型语言,编辑器中看到的文件类并不是真正的C++类,只是编译后产生的映射文件
+
+Actor的创建分为三种方式,三种方式创建的Actor生命流程有细微差距,基本流程为
+1. 构造函数调用
+2. 初始化成员变量
+3. 如有蓝图,则初始化蓝图数据
+4. 构建组件
+5. BeginPlay(标志Actor被创建到事件中)
+6. Tick
 
+> 组件是在BeginPlay方法之前构建的