设计模式需要用到面向对象的三大特性——封装、继承、多态(同名函数具有不同的状态)
UML类图 eg.—— 描述类之间的关系(设计程序之间画类图)
+: public; #: protected; -: private; 下划线: static
属性名:类型(=默认值)
方法和变量分开-------
虚函数斜体,纯虚函数在虚函数类型后=0,并且类名斜体
类与类之间的关系:
1. 继承关系(空心三角形实线,箭头指父类)
2. 关联关系(单项关联、双向关联、自关联 - 链表)用带箭头和不带箭头的实现
3. 聚合关系(整体与部分的关系,整体析构部分不析构)空心菱形实线链接,指向整体
4. 组合关系(整体析构部分析构)实心菱形实线链接
5. 依赖关系(使用关系)带箭头的虚线,指向被依赖方
类之间的关系强弱:继承(泛化)>组合>聚合>关联>依赖(类图按类间最强关系就可)
设计模式三原则
单一职责原则(面向对象):
使得类的功能尽量单一,方便管理维护,避免类的臃肿。
开放封闭原则:
对于扩展是开放的,对于修改是封闭的,增加程序可维护性可扩展性。
依赖转换原则:
高层模块不应该依赖低层模块(应用程序不直接调用API),两个都应该依赖抽象。
抽象不依赖细节,细节应该依赖抽象。(里氏代换原则)
单例模式和任务队列(类的对象只能创建出一个)
一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个实例向其他模块提供数据的全局访问。(简介访问实现对于变量的保护)
将类的默认构造函数和拷贝构造函数设为private,或者将两个函数=delete;
使类无法在外面创建对象,只能通过类名访问静态属性或者方法;
懒汉模式和饿汉模式
饿汉模式——定义类的时候创建单例对象(多线程下没有线程安全问题)
// 饿汉模式
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){
return num;
}
print(){
cout<<"单例模式的唯一实例";
}
private:
A() = default; // 默认构造
static A* num;
};
A* A::num = new A;
int main(){
A* a = A::get();
a->print();
return 0;
}
懒汉模式——什么时候使用单例对象再去创建实例(多线程下存在线程安全问题)
// 懒汉模式
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){
num = new A;
return num;
}
print(){
cout<<"单例模式的唯一实例";
}
private:
A() = default; // 默认构造
static A* num;
};
A* A::num = nullptr;
int main(){
A* a = A::get();
a->print();
return 0;
}
懒汉模式的线程安全问题
可以通过双重检查锁定解决懒汉模式的线程安全问题:1. 互斥锁(导致效率低) 2. 实例创建判定
// 懒汉模式
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){ // first check
if(num==nullptr){
lk.lock();
if(num==nullptr)num = new A; // second check
lk.unlock();
}
return num;
}
print(){
cout<<"单例模式的唯一实例";
}
private:
A() = default; // 默认构造
static A* num;
static mutex lk;
};
A* A::num = nullptr;
mutex A::lk;
int main(){
A* a = A::get();
a->print();
return 0;
}
通过原子变量(atomic - 底层控制机器指令执行顺序)解决双重检查锁定的问题;放置底层的机器指令不按理想顺序执行
// 懒汉模式
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){ // first check
A* cur = task.load();
if(cur==nullptr){
lk.lock();
cur = task.load();
if(cur==nullptr){
cur = new A; // second check
task.store(cur);
}
lk.unlock();
}
return cur;
}
print(){
cout<<"单例模式的唯一实例";
}
private:
A() = default; // 默认构造
static A* num;
static mutex lk;
static atomic<A*> task;
};
A* A::num = nullptr;
mutex A::lk;
atomic<A*> A::task;
int main(){
A* a = A::get();
a->print();
return 0;
}
使用静态局部对象解决线程安全问题
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){ // first check
static A a;
return &a;
}
print(){
cout<<"单例模式的唯一实例";
}
private:
A() = default; // 默认构造
};
int main(){
A* a = A::get();
a->print();
return 0;
}
并发执行应当等待变量完成初始化;
总结
1. 饿汉模式不存在线程安全问题
2. 懒汉模式通过双重检查锁定+原子变量或者静态局部对象(简单)可以解决线程安全问题
实践(多线程模式下的任务模型)
#include <bits/stdc++.h>
using namespace std;
// 饿汉模式
#include <bits/stdc++.h>
using namespace std;
class A{
public:
A(const A& a) = delete;
A& operator =(const A& a) = delete;
static A* get(){
return num;
}
print(){
cout<<"单例模式的唯一实例";
}
bool isempty(){
lock_guard<mutex> locker(m_mutex);
return mis.empty();
}
void add_m(int node){
lock_guard<mutex> locker(m_mutex);
mis.push(node);
}
bool minus_m(){
lock_guard<mutex> locker(m_mutex);
if(mis.empty())return false;
else{
mis.pop();
}
return true;
}
int get_m(){
lock_guard<mutex> locker(m_mutex);
if(mis.empty())return -1;
return mis.front();
}
private:
A() = default; // 默认构造
static A* num;
queue<int> mis;
mutex m_mutex;
};
A* A::num = new A;
int main(){
A *a = A::get();
// 生产者
thread t1([=](){
for(int i = 0 ; i<10 ; i++){
a->add_m(i+100);
cout<<"push data: "<<i+100<<" "<<"threadId: "<<this_thread::get_id()<<endl;
this_thread::sleep_for(chrono::milliseconds(500));
}
});
// 消费者
thread t2([=](){
this_thread::sleep_for(chrono::milliseconds(100));
while(!a->isempty()){
int cur = a->get_m();
cout<<"take data: "<<cur<<" "<<"threadId: "<<this_thread::get_id()<<endl;
a->minus_m();
this_thread::sleep_for(chrono::milliseconds(1000));
}
});
// 阻塞主线程
t1.join();
t2.join();
return 0;
}