目录
1 特殊类设计
1.1 只能在堆上创建
我们可以将无参构造函数设置为私有(不能delete,new还需要有无参构造)。
提供一个公有函数来在堆上创建类。
将拷贝构造delete,防止用户使用拷贝构造在栈上创建。
class A
{
public:
static A* getA()
{
return new A;
}
private:
A() {};
A(const A&) = delete;
};
1.2 只能在栈上创建
在c++98中,将构造函数私有,在另外提供一个公共接口在栈上创建类。
class A
{
public:
static A getA()
{
return A();
}
private:
A() {}
};
由于上面那种方式还可以利用拷贝构造在堆上创建,如下方式:
A a = getA();
A* aa = new A(a);
因为getA接口必须要用到拷贝构造,我们无法将拷贝构造私有或者delete。这时我们可以将new和delete操作符重载删除。
class A
{
public:
static A getA()
{
return A();
}
private:
void* operator new(size_t) = delete;
void operator delete(void*) = delete;
};
1.3 类不能被拷贝
直接将拷贝构造和拷贝赋值设置为私有,或者在c++11中使用delete删除这连个成员函数。
class A
{
private:
A(const A&) = delete;
A& operator=(const A&) = delete;
};
1.4 类不能被继承
最简单的做法就是在类名后加上final关键字,不过这是c++11的语法。如果只能是c++98,那么可以就构造函数设为私有
class A final
{};
class A
{
private:
A();
};
2 单例模式
2.1 饿汉模式
饿汉模式,就是在主函数执行前就已经创建好了实例。这样的做法缺点是无法在多单例创建时控制创建顺序,而且提早创建好实例会导致进程启动慢。其优点就是实现起来结构简单。
其实现就是,将一般的构造、赋值都设置为私有或delete,另外提供公共接口来得到实例。需要注意公共接口设置为静态,否则没有实例来执行该接口创建第一个实例。私有属性_instance只能有一个,也应设置为静态的。
class Singleton
{
public:
static Singleton& getInstance()
{
return _instance;
}
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton _instance;
};
Singleton Singleton::_instance;
2.2 饱汉
饱汉模式下下,在执行主程序前还没有创建实例,只有一个类类型的指针。在等到需要调用getInstance函数时才会进行实例创建。这样做的好处时,在进程启动时还未创建实例,加快进程启动速度。同时在多单例需要创建的情况,可以堆创建顺序进行控制。
实现中需要注意的是,在判断单例是否已经创建后相应执行操作时,如果是多线程存在线程安全问题,需要加锁保证互斥。同时由于争夺锁时间消耗很大,为了提高代码效率,选择双判断减少对锁的争夺是更好的选择。
class Singleton
{
public:
static Singleton* getInstance()
{
if (_pInstance == nullptr)
{
unique_lock<mutex> lock(_mtx);
if (_pInstance == nullptr)
{
_pInstance = new Singleton;
}
}
return _pInstance;
}
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* _pInstance;
static mutex _mtx;
};
Singleton* Singleton::_pInstance = nullptr;
mutex Singleton::_mtx;