网站开发设计注册,wordpress控制菜单是否显示,百度广告费,贵州做团队培训的网站文章目录 单例模式特殊类的设计单例模式饿汉模式懒汉模式懒汉VS饿汉懒汉的线程安全单例对象的释放 单例模式
认识单例模式之前先认识一下几种常见的特殊类的设计。
特殊类的设计 设计一个类 只能再堆上创建对象 只能再堆上创建#xff0c;则通过new来创建对象。 将类的构造函… 文章目录 单例模式特殊类的设计单例模式饿汉模式懒汉模式懒汉VS饿汉懒汉的线程安全单例对象的释放 单例模式
认识单例模式之前先认识一下几种常见的特殊类的设计。
特殊类的设计 设计一个类 只能再堆上创建对象 只能再堆上创建则通过new来创建对象。 将类的构造函数设为私有以防止外部直接创建对象。提供一个静态成员函数用于在堆上创建对象并返回指向该对象的指针在类的析构函数中释放对象的内存以确保对象在不再需要时被正确销毁。 class HeapOnly {public:// 获取堆上对象的静态成员函数static HeapOnly* CreateObj() {return new HeapOnly();}// 删除拷贝构造函数和赋值运算符以确保只能通过 create() 创建对象HeapOnly(const HeapOnly) delete;HeapOnly operator(const HeapOnly) delete;private:// 将构造函数设为私有以防止外部直接创建对象HeapOnly() {// 可以在这里进行初始化操作}// 在析构函数中释放对象的内存~HeapOnly() {// 可以在这里进行资源释放操作}}; HeapOnly 类的构造函数是私有的外部无法直接调用。 向外提供的CreateObj方法必须定义为static成员函数。因为外部调用该接口就是为了获取对象的而非静态成员函数必须通过对象才能调用静态成员函数直接使用类名::函数名即可调用。 将拷贝构造函数和赋值运算符重载函数之间禁用也可以设置为私有属性以防通过拷贝和赋值创建对象。 使用示例 int main() {// 创建堆上的对象HeapOnly* obj HeapOnly::create();// 使用对象...// 删除对象delete obj;return 0;
} 设计一个类 只能再栈上创建对象 只能再栈上创建对象说明不能通过new创建对象也不能定义为static对象。创建方式如下 将类的构造函数设为私有防止外部直接调用构造函数在堆上创建对象。在类中定义一个静态成员函数用于创建对象并返回该对象的引用或指针。 class StackOnly {
public:// 获取栈上对象的静态成员函数static StackOnly create() {// 在静态成员函数中创建对象返回对象的引用static StackOnly instance;return instance;}// 删除拷贝构造函数和赋值运算符以防止通过拷贝或赋值创建对象StackOnly(const StackOnly) delete;StackOnly operator(const StackOnly) delete;private:// 将构造函数设为私有防止外部直接创建对象StackOnly() {// 可以在这里进行初始化操作}};
int main() {// 创建栈上的对象StackOnly obj StackOnly::create();return 0;
} 将StackOnly类的构造函数私有化外部无法直接调用。限制了通过new创建对象和static对象。 静态局部对象的引用是存储在栈上的。 将拷贝构造函数和赋值运算符重载函数之间禁用也可以设置为私有属性以防通过拷贝和赋值创建对象。 设计一个类 不能被拷贝 将拷贝构造函数和赋值运算符设置为私有或者直接使用delete禁用即可。 class NonCopyable {
public:NonCopyable() {} // 默认构造函数// 禁用拷贝构造函数和拷贝赋值运算符NonCopyable(const NonCopyable) delete;NonCopyable operator(const NonCopyable) delete;
};int main() {NonCopyable obj1;// NonCopyable obj2 obj1; // 这行会导致编译错误因为拷贝构造函数被删除了// NonCopyable obj3;// obj3 obj1; // 这行会导致编译错误因为拷贝赋值运算符被删除了return 0;
}设计一个类 不能被继承 将该类的构造函数设置为私有即可因为子类对象构造时必须先调用父类构造构造父类那一部分成员。将其私有化后子类对象创建对象时无法调用父类构造函数进行初始化。 class NonInherit
{
public:static NonInherit CreateObj(){return NonInherit();}
private://将构造函数设置为私有NonInherit(){}
};也可以使用C11提供的关键字final。别final修饰的类无法被继承 class NonInherit final
{//...
}; 设计一个类 只能创建一个对象 一个类只能创建一个对象将其称之为单例模式。
单例模式
单例模式是一种设计模式Design Pattern设计模式就是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式的目的就是为了可重用代码、让代码更容易被他人理解、保证代码可靠性程序的重用性。
单例模式指的就是一个类只能创建一个对象该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。
单例模式有两种实现方式分别是饿汉模式和懒汉模式
饿汉模式
单例模式的实现方式
私有化构造函数确保其他类无法直接实例化该类只能通过指定的方法获取类的实例。私有静态成员变量保存类的唯一对象。并在程序入口之前完成单例对象的初始化。公有静态方法提供对唯一实例的访问。
class Singleton
{
public: //公有静态方法static Singleton* GetInstace(){return _inst;}//禁用拷贝构造和赋值运算符Singleton(const Singleton s) delete;Singleton operator(const Singleton) delete;
private://私有化构造函数Singleton(){}//私有静态成员变量static Singleton* _inst;
};
Singleton* Singleton::_inst new Singleton;//静态成员变量的初始化int main()
{//获取单例对象Singleton* ojb Singleton::GetInstace();
}懒汉模式
懒汉模式的实现方式
私有化构造函数确保其他类无法直接实例化该类只能通过指定的方法获取类的实例。私有静态成员变量保存类的唯一对象将其初始化为空。公有静态方法提供对唯一实例的访问。如果实例不存在则创建一个新实例并返回如果实例已存在则返回现有实例
// 懒汉模式
class Singleton
{
public://公有静态方法static Singleton* GetInstance(){if (_inst nullptr){_inst new Singleton;}return _inst;}//禁用拷贝构造和赋值运算符Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;
private://私有构造函数Singleton(){}//私有静态成员变量static Singleton* _inst;
};
Singleton* Singleton::_inst nullptr;int main()
{//获取单例对象Singleton* obj Singleton::GetInstance();return 0;
}懒汉VS饿汉
饿汉 在类加载时就创建实例。无论是否使用该实例都会在类加载时被创建。如果这个类很大会影响程序的启动速度。在mian程序加载之前就会被实例化不存在线程安全问题使用简单 懒汉 在需要时才创建实例。也就是说实例的创建延迟到第一次使用时才发生。在多线程环境下需要考虑线程安全性特别是在第一次获取实例时可能会存在多个线程同时创建实例的问题。需要通过加锁或者双重检查锁定等技术来确保线程安全性。使用复杂
懒汉的线程安全
上面写的懒汉单例并不是线程安全的在多线程场景下存在线程安全的问题。GetInstance函数第一次调用时需要对_inst进行写入操作这个操作不是线程安全的多个线程可能同时调用GetInstance进行写入如果不对此过程进行加锁保护多线程下就会各自创建出一个对象。
对于饿汉模式来讲不存在线程安全的问题因为在main函数之前就已经创建好对象了。
在GetInstance中创建单例对象进行加锁保护。
static Singleton* GetInstance()
{if (_inst nullptr){_mtx.lock();if (_inst nullptr){_inst new Singleton;}_mtx.unlock();}return _inst;
}进行双重检查加锁来保证线程安全。双重检查会提高效率如果是单检查如下
static Singleton* GetInstance()
{_mtx.lock();if (_inst nullptr){_inst new Singleton;}_mtx.unlock();return _inst;
}这样每次访问单例对象时都要进行加锁加锁之后才能进行判断。双检查之后只有第一次访问单例对象时才需要加锁。后续访问时无需加锁了。提高了效率。
单例对象的释放
单例对象创建后一般在整个程序运行期间都可能会使用所以我们可以不考虑单例对象的释放程序正常结束时会自动将资源归还给操作系统。
有些场景下可能需要提前释放单例对象可以参考一下方式
提供一个公有的DelInstance函数在该函数中进行单例对象释放的操作当不需要单例对象时调用此函数进行单例对象的释放。函数如下
static void DelInstance()
{_mtx.lock();if (_inst ! nullptr){delete _inst;_inst nullptr;}_mtx.unlock();
}释放操作只能调用一次单检查加锁足够了无需双检查加锁。
注意单例模式中的单例对象和互斥锁和提供的公有方法都是static的。
首先要明确static修饰成员变量和成员函数的特征 静态成员变量 所有类的对象共享静态成员变量的值。在类声明中声明为静态成员变量但在类外部必须在类外进行定义和初始化。静态成员变量的内存只分配一次直到程序结束才会被释放。可以在类外部通过类名和作用域解析运算符::来访问静态成员变量。 静态成员函数 不与任何特定的对象相关联可以直接通过类名调用。静态成员函数没有隐含的this指针。静态成员函数不能访问非静态成员变量和非静态成员函数除非它们是同一个类中的静态成员。由于它们不与特定对象相关联因此无法在静态成员函数中使用this指针。
在单例模式中需要将实例保存在静态成员变量中是因为
全局可访问性静态成员变量属于类而不是对象因此可以被该类的所有对象访问。这符合单例模式的要求即只有一个实例并且可以在任何地方访问到该实例。生命周期与类相同静态成员变量随着类的加载而初始化而不是随着对象的创建而初始化。这意味着无论是否存在对象静态成员变量都会在类加载时创建从而保证了在整个应用程序生命周期内只有一个实例存在。
因此将单例实例保存在静态成员变量中能够满足单例模式的要求确保了实例的全局可访问性和唯一性。