柚子快报激活码778899分享:c++ Qt 之单例模式
单例模式场景 创建方式 singleton.h singleton.cpp main.c 优化 QScopedPointer 单例+智能指针 优化后的Singleton.h 优化后的Singleton.cpp 优化后的main.cpp QScopedPointer 后记 QScopedPointer::reset() QScopedPointer::take()和QScopedPointer::data() QScopedArrayPointer 单例模式场景 只需要一个对象的场景,比如系统日志,设备,读写配置。 比如GUI log, 一个班只有一个班主任,
只能创建一个对象,并提供访问它的唯一全局访问点, 避免频繁的创建与销毁 自己创建自己唯一的实例 创建方式 singleton.h #ifndef SINGLETON #define SINGLETON class Singleton {
public: QString getInstanceName() const; static Singleton& getInstance(); //获取singleton的唯一对象 static void release(); private: Singleton() //私有的构造函数 ~Singleton(); //析构函数 Singleton(const Singleton&other); //拷贝构造函数 Singleton& operator=(const Singleton&other); //赋值运算操作符 static QMutex mutex; static Singleton* instance; //singleton 全局唯一的变量 }
#endif singleton.cpp #include "signleton.h"
QMutex Singleton::mutex; Singleton* Singleton::instance = NULL; Singleton::Singleton() { qDebug()<<"Singleton()"; }
Singleton::~Singleton() { qDebug()<<"~Singleton()" } Singleton& Singleton::getInstance() { /* * 双重锁定,减少每次开销,只有越过if(NULL == instance) 才需要锁定,极低概率 */ if(NULL == instance) { mutex.lock(); //线程安全 if(NULL == instance) { instance = new Singleton(); } mutex.unlock(); } return *instance; }
void Singleton::release() { if(instance != NULL) { mutex.lock(); delete instance; instance = NULL; mutex.unlock(); } } QString Singleton::getInstanceName() const { return "this is test for singleton"; }
main.c #include "ConfigUtil.h" #include
int main(int argc, char *argv[]) { Q_UNUSED(argc) Q_UNUSED(argv)
qDebug() << Singleton::getInstance().getInstanceTest(); qDebug() << Singleton::getInstance().getInstanceTest();
Singleton::release(); // 程序结束时需要手动析构 Singleton 的对象 return 0; } 输出结果
Singleton()() “this is test for singleton” “this is test for singleton” ~Singleton()
优化 上述程序退出时,需要手动释放资源。如果系统中单例较多,那么手动释放变成一个非常大的工作量,而且有可能会出现遗漏的情况。
QScopedPointer QScopedPointer 类似于 C++ 11 中的 unique_ptr。当我们的内存数据只在一处被使用,用完就可以安全的释放时就可以使用 QScopedPointer
uniqure_ptr Demo #include
struct Task { int mId; Task(int id ) :mId(id) { std::cout << "Task::Constructor" << std::endl; } ~Task() { std::cout << "Task::Destructor" << std::endl; } };
int main() { // 通过原始指针创建 unique_ptr 实例 std::unique_ptr
//通过 unique_ptr 访问其成员 int id = taskPtr->mId; std::cout << id << std::endl;
return 0; }
通常Qt从C++开发者手里接管了枯燥无味的垃圾回收工作,例如通过Qt自身的隐式共享,或者用QObject父子关系模型。 但是,每一次我们需要在堆上分配内存的时候,就有压力了 —— 我们需要在哪儿删除它,我们如何确保不会有内存泄露?
为了解决这个问题,QScopedPointer 诞生了。 当要超出作用域时,QScopedPointer 便会自动的删除它所指向的对象(的内存): The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon destruction.
void foo() { QScopedPointer
public: QString getInstanceName() const; static Singleton& getInstance(); //获取singleton的唯一对象 static void release(); private: Singleton() //私有的构造函数 ~Singleton(); //析构函数 Singleton(const Singleton&other); //拷贝构造函数 Singleton& operator=(const Singleton&other); //赋值运算操作符 static QMutex mutex; static QScopedPointer
#endif 优化后的Singleton.cpp QMutex Singleton::mutex; QScopedPointer
Singleton& Singleton::getInstance() { if (instance.isNull()) { mutex.lock(); if (instance.isNull()) { instance.reset(new ConfigUtil()); //QScopedPointer的reset方法 } mutex.unlock(); }
return *instance.data(); //返回指向对象的常量指针 }
优化后的main.cpp #include "Singleton.h" #include
int main(int argc, char *argv[]) { Q_UNUSED(argc) Q_UNUSED(argv)
qDebug() << Singleton::getInstance().getInstanceTest(); qDebug() << Singleton::getInstance().getInstanceTest(); //Singleton::release(); //不需要再手动释放了 return 0; } QScopedPointer 后记 QScopedPointer::reset() Some operators are missing by design, for example the assignment operator: 一些常有的操作(操作符重载),在设计上是有意没有实现,例如 赋值运算符( = );
QScopedPointer
void QScopedPointer::reset(T *other = Q_NULLPTR):delete目前指向的对象,调用其析构函数,将指针指向另一个对象other,所有权转移到other
QScopedPointer::take()和QScopedPointer::data() 使用QScopedPointer智能指针动态创建的对象,一旦出了作用域就会 被自动释放并置空,那么如果需要函数返回值怎么办呢?
int*foo() { QScopedPointer
int*foo() { QScopedPointer
int main(int argc, char *argv[]) { QApplication a(argc, argv); QScopedPointer
return a.exec(); } T *QScopedPointer:: data() const返回指向对象的常量指针,QScopedPointer仍拥有对象所有权。所以通过data()返回过后就被自动删除了,从而导致mian函数中的p1变成了野指针,程序崩溃。
T *QScopedPointer:: take()也是返回对象指针,但QScopedPointer不再拥有对象所有权,而是转移到调用这个函数的caller,同时QScopePointer对象指针置为NULL
QScopedArrayPointer 如果是通过malloc分配的内存,或者通过 new[] 分配的数组呢?
为方便起见,我们用一个QScopedArrayPointer来指向那些需要被delete[]删除的对象。 同样为了方便,它还具有operator [] 运算符重载(取数组成员) ,所以我们可以这样写:
void foo() { QScopedArrayPointer
/************************************************************************************
最简单的写法:c++
1
2
3
4
5
static MyClass* MyClass::Instance()
{
static MyClass inst;
return &inst;
}
过去很长一段时间一直都这么写,简单粗暴有效。可是直接声明静态对象会使编译出的可执行文件增大,也有可能出现其余的一些问题,因此利用了Qt自带的智能指针QScopedPointer和线程锁QMutex,改为了须要时才动态初始化的模式:安全
1
2
3
4
5
6
7
8
9
10
11
12
13
static MyClass* MyClass::Instance()
{
static QMutex mutex;
static QScopedPointer
if (Q_UNLIKELY(!inst)) {
mutex.lock();
if (!inst) {
inst.reset(new MyClass);
}
mutex.unlock();
}
return inst.data();
}
既保证了线程安全又防止了内存泄漏,效率也没下降太多,简直完美。函数
惋惜每次都要重复这么几行实在麻烦,因而写了一个模板类:spa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template
class Singleton
{
public:
static T* Instance()
{
static QMutex mutex;
static QScopedPointer
if (Q_UNLIKELY(!inst)) {
mutex.lock();
if (!inst) {
inst.reset(new T);
}
mutex.unlock();
}
return inst.data();
}
};
使用的时候直接这样——线程
1
MyClass* inst = Singleton
除了用模板类,还能够利用c++中强大的宏:指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define DECLARE_SINGLETON(Class) \
Q_DISABLE_COPY(Class) \
public: \
static Class* Instance() \
{ \
static QMutex mutex; \
static QScopedPointer
if (Q_UNLIKELY(!inst)) { \
mutex.lock(); \
if (!inst) inst.reset(new Class); \
mutex.unlock(); \
} \
return inst.data(); \
}
而后声明的时候,填加一行这个宏:code
1
2
3
4
5
class MyClass
{
DECLARE_SINGLETON(MyClass); // 声明单例模式
//...
}
好评好评。对象
固然,为了要保证真的是单例模式,还要把构造函数限制为private,否则之后何时忘记了这码事,在外面又new了一下就很差了。blog
另外Qt自己自带了一个宏Q_GLOBAL_STATIC,也有相似单例模式的效果,QThreadPool::globalInstance()函数的实现就是利用了这个宏。不过它的主要用处是声明全局变量,和Singleton仍是有差异的。内存
/**********************************************************
qt既可以开发GUI程序,也可用于开发非GUI程序,是一个由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。那qt使用的什么设计模式?下面来我们就来给大家讲解一下。
其实qt的设计模式有很多种,包括单例模式、观察者模式、适配器模式等,我们今天要讲的就是qt单例模式:
单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面我们来看下有哪几种实现方式吧。
核心代码:构造方法私有化,private。
1、懒汉式
懒汉式,顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
2、饿汉式
饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
3、双检锁
双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
4、静态内部类
静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
5、枚举
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。
设计模式的六大原则;
1、开闭原则
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则
里氏代换原则是面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
这就是qt单例模式的五种主要写法,一般情况下,懒汉式都比较少用;饿汉式和双检锁都可以使用,可根据具体情况自主选择;最后大家如果想要了解更多json相关知识,敬请关注奇Q工具网。
柚子快报激活码778899分享:c++ Qt 之单例模式
发表评论