设计模式是一种解决特定问题的预定义优秀代码框架,相较于自己摸索解决方案,它具有以下优点:
设计模式的分类:
23种设计模式:
在此之前, 先了解一下 static 关键字
static 用法 | 作用 | 补充说明 |
---|---|---|
静态局部变量 | 变量不会在函数调用结束后销毁,值会保留 变量在函数调用结束后依然存在(局部静态变量) |
- 仅在定义它的函数内可见 - 初次声明时初始化一次,后续调用不会重新初始化 -会从栈区移动到数据区 |
静态全局变量 | 仅在当前文件可见,防止命名冲突 限制变量作用范围,使其仅在当前文件可见(内部链接) |
- 作用域限制在定义它的文件中,不会污染其他文件的命名空间 |
静态成员变量 | 属于类,而不是对象,所有对象共享 变量属于类,而不是对象,所有对象共享一份数据 |
- 需在类内声明, 类外部定义和初始化 (ClassName::variableName = value; )- 通过 类名::变量名 访问 |
静态成员函数 | 属于类,不依赖对象,不能访问非静态成员 函数属于类,而不是对象,不能访问非静态成员 |
- 无 this 指针,不能访问实例成员变量/方法- 通过 类名::函数名 访问-其内部只能访问 静态 成员, 不能访问不同的成员 |
静态全局函数 | 仅在当前文件可见,防止命名冲突 限制函数作用范围,使其仅在当前文件可见 |
- 仅在定义它的文件中可见,外部文件无法调用 |
单例模式:一个类不管创建多少次对象,永远只能得到该类类型的唯一一个实例对象,那么设计一个单例就必须要满足下面几个条件:
单例模式分类:
单例模式的应用场景举例:
什么是饿汉式单例模式?
饿汉式单例(Hungry Singleton)是一种在类加载时就立即实例化对象的单例模式
还没有获取实例对象, 实例对象 就已经产生了
饿汉式 一定是 线程安全的?
因为 静态的成员变量 在数据段(.data), 数据段 程序已启动 就初始化好了
饿汉式单例本身在静态变量的初始化中一般是线程安全的,但需要注意静态初始化顺序的问题,尤其在多线程环境和动态库情况下。为了确保绝对的线程安全,可以使用一些额外的同步机制(例如 std::call_once
或互斥锁)来避免潜在的竞争条件。
缺点:
资源浪费(主要):即使单例未被使用,类加载时就会创建实例,导致不必要的资源消耗。
不支持延迟初始化:实例在程序启动时就创建,无法根据需要延迟创建。
初始化顺序问题:在静态变量初始化时可能会引发顺序问题,导致错误或未定义行为。
无法处理复杂并发需求:对复杂资源或多线程环境下的初始化需求处理不足。
生命周期管理困难:无法灵活控制单例的销毁和生命周期,难以处理动态加载和卸载。
代码示例:
#include
#include
#include
#include
#include
#include
using namespace std;
class Singleton
{
public:
// 3. 提供一个静态方法返回唯一实例的引用
static Singleton* getInstance() { return &instance; }
private:
static Singleton instance; // 2. 定义该类的唯一实例对象(使用静态变量,在数据段上)
Singleton() { cout << "Singleton()" << endl; } // 1.构造函数私有化
~Singleton() { cout << "~Singleton()" << endl; }
// 4. 删除拷贝构造和赋值函数 - 不能让他使用默认的
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance; // 类外初始化静态成员变量
int main()
{
// 还没有调用, 实例对象就创建了
Singleton* p1 = Singleton::getInstance(); // 静态成员函数, 需要使用 类名::函数名 使用
Singleton* p2 = Singleton::getInstance();
Singleton* p3 = Singleton::getInstance();
//Singleton t = *p1; // 单例模式不允许, 需要删除类内 拷贝和赋值函数
return 0;
}
测试结果:
Singleton()
008EE1E0
008EE1E0
008EE1E0
~Singleton()
可以看到,该程序通过饿汉式单例模式实现了单例的创建和管理,确保了整个程序中Singleton
类只有一个实例。程序输出验证了单例模式的正确性,所有获取的引用都指向同一个实例
什么是懒汉式单例?
懒汉式单例(Lazy Singleton)是单例设计模式的另一种实现方式,与饿汉式单例不同,它是在第一次需要实例化时才创建对象,而不是在程序启动时就立即创建。
代码示例:
#include
#include
#include
#include
#include
#include
using namespace std;
class Singleton
{
public:
static Singleton* getInstance()
{
if (instance == nullptr) //#3 延迟实例化
{
instance = new Singleton();
}
return instance;
}
static void destroyInstance()
{
delete instance;
}
private:
static Singleton* instance; // #1 修改为指针, 更好的是使用智能指针, 为了自动析构, 这种一般指针, 还得写 销毁函数, 手动释放
Singleton() { cout << "Singleton()" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance=nullptr; // #2 初始化为空
int main()
{
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
Singleton* p3 = Singleton::getInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
Singleton::destroyInstance();
return 0;
}
测试结果:
Singleton()
00F20548
00F20548
00F20548
~Singleton()
可重入函数(Reentrant Function)是指在多线程或中断处理的环境下,函数能够安全地被多个线程或中断同时调用,即使在执行过程中该函数被再次调用,也能保证不会产生数据不一致或程序崩溃
上面实现的懒汉式单例模式是有问题的
其中getInstance
函数不是可重入函数
根源在于:
会有线程安全的问题getInstance
有三个步骤:开辟内存、构造对象、给instance
赋值(编译器翻译成汇编指令的顺序或者也可以是开辟内存、给instance
赋值、构造对象), 不是原子操作
无论两种情况中的哪种,只要是线程一在给instance
赋值之前,如果有线程二进入此函数,就会造成线程不安全。
加锁的懒汉单例
#include
#include
#include
#include
#include
#include
using namespace std;
std::mutex mtx;
class Singleton
{
public:
static Singleton* getInstance()
{
//lock_guard guard(mtx); // 这个位置 锁力度太大
if (instance == nullptr)
{
lock_guard<std::mutex> guard(mtx); // 只在第一次实例化时 加锁, 这有这一块 是步骤多的, 不是原子操作
// 加锁后, 还要判断
if (instance == nullptr)
{
instance = new Singleton();
}
}
return instance;
}
static void destroyInstance()
{
delete instance;
}
private:
static Singleton *volatile instance; // 同时要保证 不缓存
Singleton() { cout << "Singleton()" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton * volatile Singleton::instance=nullptr;
int main()
{
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
Singleton* p3 = Singleton::getInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
Singleton::destroyInstance();
return 0;
}
给instance
加一个volatile
,这是给指针加的(不是给指针的指向加的)。好处是当一个线程对instance
赋值时,其他线程马上均能看到instance
的改变。因为线程现在已经不对共享变量进行缓存了,大家看的都是其原始内存中的值
非互斥锁的线程安全懒汉式单例模式
#include
#include
#include
#include
#include
#include
using namespace std;
std::mutex mtx;
class Singleton
{
public:
static Singleton* getInstance()
{
static Singleton instance;
return &instance;
}
private:
Singleton() { cout << "Singleton()" << endl; }
~Singleton() { cout << "~Singleton()" << endl; }
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main()
{
Singleton* p1 = Singleton::getInstance();
Singleton* p2 = Singleton::getInstance();
Singleton* p3 = Singleton::getInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
函数静态局部变量的初始化在汇编指令上已经自动添加线程互斥指令了,也就是已经线程安全了!(linux下 gdb调试, 看底层汇编, 看老师博客) vs里看汇编是看不到的
在 C++11 及以后的标准中,局部静态变量的初始化是 线程安全的,
对于static静态局部变量的初始化,编译器会自动对它的初始化进行加锁和解锁控制,使静态局部变量的初始化成为线程安全的操作,不用担心多个线程都会初始化静态局部变量,因此上面的懒汉单例模式是线程安全的单例模式!
主要用于 创建对象 非常多的 时候
先了解一下工厂模式
工厂模式是一个比较常用的创建型设计模式,主要是封装了对象的创建
,其中可以细分为三种:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)
简单工厂模式:
代码核心:
枚举类和简单工厂类
#include
#include
using namespace std;
class Car
{
public:
Car(string name) : _name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class Bmw : public Car
{
public:
Bmw(string name) : Car(name) {}
void show() override { cout << "我是宝马:" << _name << endl; }
};
class Audi : public Car
{
public:
Audi(string name) : Car(name) {}
void show() override { cout << "我是奥迪:" << _name << endl; }
};
enum CarType // 枚举值
{
BMW, AUDI
};
class SimpleFactory // 简单工厂的 核心
{
public:
static Car* createCar(CarType ct)
{
switch (ct)
{
case BMW:
return new Bmw("X3");
case AUDI:
return new Audi("A8");
default:
cerr << "传入参数不正确" << ct<< endl;
return nullptr;
}
}
};
int main()
{
// 买车, 不需要知道车是怎么构造的
//Car* p1 = new Bmw("Bmw X1"); //使用的时候, 需要记住 很多 派生类名
// Car* p1 = SimpleFactory::createCar(BMW);
// Car* p2 = SimpleFactory::createCar(AUDI);
// if (p1) p1->show();
// if (p2) p2->show();
// delete p1;
// delete p2;
// 改成智能指针,自动释放资源, 注意需要头文件memory
unique_ptr<Car> p1(SimpleFactory::createCar(BMW));
unique_ptr<Car> p2(SimpleFactory::createCar(AUDI));
if (p1) p1->show();
if (p2) p2->show();
return 0;
}
现在,简单工厂模式就实现了,但是这种方法有一些缺陷,因为同一个工厂不会又造宝马又造奥迪,又造手机又造键盘的,这一般不符合实际的情况,而且最重要的是这个工厂不符合“开闭原则”,如果我们要增删车辆,就要修改接口,这样很不好。
基于这样的缺陷,接下来看看工厂方法模式
工厂方法模式:
代码核心: 工厂方法类(虚构造函数)及其派生类
#include
#include
using namespace std;
class Car
{
public:
Car(string name) : _name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class Bmw : public Car
{
public:
Bmw(string name) : Car(name) {}
void show() override { cout << "我是宝马:" << _name << endl; }
};
class Audi : public Car
{
public:
Audi(string name) : Car(name) {}
void show() override { cout << "我是奥迪:" << _name << endl; }
};
// 工厂方法
class Factory
{
public:
virtual Car* createCar(string name) = 0; // 工厂方法
};
// 宝马工厂
class BMWFactory : public Factory
{
public:
Car* createCar(string name) override { return new Bmw(name); }
};
// 奥迪工厂
class AudiFactory : public Factory
{
public:
Car* createCar(string name) override { return new Audi(name); }
};
int main()
{
// 这个构建有点麻烦, 慢慢看, 先创建相应的工厂, 在使用工厂创建相应的产品
//unique_ptr bmwfty(new BMWFactory());
unique_ptr<Factory> bmwfty = make_unique<BMWFactory>() ; // c++14 new在外面是不行的
unique_ptr<Factory> audifty(new AudiFactory());
unique_ptr<Car> p1(bmwfty->createCar("X6"));
unique_ptr<Car> p2(audifty->createCar("A8"));
p1->show();
p2->show();
return 0;
}
现在符合“开闭原则”,如果现在想加一个奔驰工厂,直接增加BenzFactory
类就行了
抽象工厂模式:
工厂方法与抽象工厂区别:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。因此,工厂方法模式可以被视为抽象工厂模式的一个特例或简化形式
当一个工厂不止生产车, 还生产对应的灯, 即一组产品
还有缺点: 基类添加新产品时, 派生类也得加
#include
#include
using namespace std;
class Car {
public:
virtual void show() = 0;
};
class Light {
public:
virtual void type() = 0;
};
// ===================================================
class BMWCar : public Car {
public:
void show() override { cout << "BMW car" << endl; }
};
class BMWLight : public Light {
public:
void type() override { cout << "BMW Light" << endl; }
};
// ===================================================
class AudiCar : public Car {
public:
void show() override { cout << "Audi car" << endl; }
};
class AudiLight : public Light {
public:
void type() override { cout << "Audi Light" << endl; }
};
// ===================================================
class AbstractFactory {
public: // 产品簇
virtual Car* createCar() = 0;
virtual Light* createLight() = 0;
};
class BMWFactory : public AbstractFactory {
public:
Car* createCar() override { return new BMWCar(); }
Light* createLight() override { return new BMWLight(); }
};
class AudiFactory : public AbstractFactory {
public:
Car* createCar() override { return new AudiCar(); }
Light* createLight() override { return new AudiLight(); }
};
// ===================================================
int main()
{
// 创建具体工厂
unique_ptr<AbstractFactory> bmwfty(new BMWFactory());
unique_ptr<AbstractFactory> audifty(new AudiFactory());
// 使用工厂创建产品
unique_ptr<Car> bmwCar(bmwfty->createCar());
unique_ptr<Light> bmwLight(bmwfty->createLight());
unique_ptr<Car> audiCar(audifty->createCar());
unique_ptr<Light> audiLight(audifty->createLight());
if (bmwCar) bmwCar->show();
if (bmwLight) bmwLight->type();
if (audiCar) audiCar->show();
if (audiLight) audiLight->type();
return 0;
}
简单工厂 (Simple Factory):
new
对象,不用了解对象创建的详细过程。工厂方法 (Factory Method):
抽象工厂 (Abstract Factory):
AbstractFactory
。