今天忙活了一天写了一个线程池,写完我才发现单例模式的重要性,做如下学习记录
private
,这样外部就无法拷贝了class A
{
private:
A(const A& x) ;
A& operator=(const A& x);
};
delete
我们可以直接将拷贝构造和赋值重载函数删除class A
{
public:
A(const A& x) = delete;
A& operator=(const A& x) = delete;
};
首先想明白一个问题:构造函数是否需要删除?
答案是明显的:不能,因为在堆上开辟空间new
需要调用构造函数,如果删除了就无法开辟空间了,我们这里要做的是将构造函数隐藏,并封装一个函数使其只能调用new
来构造对象
class A
{
public:
static A* heap_only()
{
return new A;
}
private:
A()
{
//....
}
};
这个思路就是将operator new
或者将 operator delete
给删除或者放到private
作用域中
class A
{
public:
void* operator new (size_t a) = delete;
void operator delete(void *) = delete;
};
private
作用域之中,这是因为子类的构造函数一定会调用基类的构造函数,如果基类的构造函数不可见,那么它就无法被继承final
去修饰
class A final
{
//....
};
设计模式:
设计模式我认为是工程文件的一种技巧,是一种被反复使用、多数人知晓、经过分类的、代码经验的总结
单例模式:
一个类只能创建一个对象,这就是单例模式。单例模式的类保证系统中该类只有一个实例,并可以被所有模块访问这一个实例。
我想先看饿汉模式的设计:
class A
{
public:
static A& gey_singleton()
{
return singleton;
}
A(const A&) = delete;
A& operator=(const A&) = delete;
private:
A()
{
//....
}
static A singleton;
};
A A::singleton;
有如下几个要点:
singleton
必须是static
对象,这样保证了一个类只有一个对象singleton
能构造出对象singleton
使得类外部能够拿到这个单例对象,但是这个接口函数必须设计成静态成员函数,如果不是静态的就会造成——因为你想调用这个成员函数就必须有这个类的对象,如果你像要得到这个类的对象就必须调用这个类的成员函数。所以我们这里定义成静态成员变量直接使用类域就可以调用class A
{
public:
static A* get_singleton()
{
if (singleton == nullptr)
singleton = new A;
return singleton;
}
//防止拷贝
A(const A&) = delete;
A& operator=(const A&) = delete;
private:
A()
{
//..
}
static A* singleton;
};
A* A::singleton = nullptr;
懒汉模式的设计思路与饿汉模式设计思路大体上是一致的,唯一区别就是懒汉模式的单例对象singleton
是一个指针,而饿汉模式的单例对象singleton
是一个对象。
区别
首先补充一点知识:静态和全局数据在编译链接之后的可执行文件之中就是存在的,而我们堆栈则是在运行的时候才生成的。可执行文件运行的时候要经历一步加载,由加载器来完成该步骤——也就是创立进程的结构、建立虚拟内存到物理内存的映射。如果一个可执行文件越大,加载的速度也就越慢。
上面的代码实际上还是有一些不足的:
以饿汉模式为例,单例模式算是一个公共资源,所有模块都能访问就会有线程安全问题,所以我们要设计一个线程安全的单例模式
class A
{
public:
static A* get_singleton()
{
//双判断可以避免单例已经被创建,但是任然有多个线程申请锁、判断、释放锁的无用开销
if (singleton == nullptr)
{
m.lock();
if (singleton == nullptr)
singleton = new A;
m.unlock();
}
return singleton;
}
A(const A&) = delete;
A& operator=(const A&) = delete;
private:
A()
{
//..
}
static A* singleton;
static mutex m;
};
A* A::singleton = nullptr;