柚子快报激活码778899分享:c++ Qt 之单例模式

http://www.51969.com/

单例模式场景 创建方式 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 #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 taskPtr(new Task(23));

    //通过 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 i(newint(42)); //… if (someCondition) return;  // 我们在堆上分配的一个整数将在这个位置, // …  } // 或者这个位置被自动的删除 单例+智能指针 优化后的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 QScopedPointer instance; //静态的QScopedPointer的变量instance      }

#endif 优化后的Singleton.cpp QMutex Singleton::mutex; QScopedPointer Singleton::instance;

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 i(newint(42)); i = newint(43);            // 错误 i.reset(new int(43));     // 正确 我们设想:“reset” 看起来足够吓人了,以致于能够使读者认识到,这样会删除旧的对象,并使QScopedPointer指向新的对象.

void QScopedPointer::reset(T *other = Q_NULLPTR):delete目前指向的对象,调用其析构函数,将指针指向另一个对象other,所有权转移到other

QScopedPointer::take()和QScopedPointer::data() 使用QScopedPointer智能指针动态创建的对象,一旦出了作用域就会 被自动释放并置空,那么如果需要函数返回值怎么办呢?

int*foo() {     QScopedPointer i(newint(42));     //…     return i; // thankfully, this does not compile.  谢天谢地,这无法通过编译 } 在函数返回的同时,我们的对象会被删除,因为QscopedPointer离开其作用域了. 我们将会返回一个悬垂指针,有可能导致程序崩溃。然后,当我们可以通过调用take()成员函数,告诉QScopedPointer它的工作完成了,从而接手它所指向的堆的控制权,我们的函数可能会像如下这样:

int*foo() {     QScopedPointer i(newint(42));     …     if (someError)     return 0; // our integer is deleted here     return i.take(); // from now on, our heap object is on its own. } 再来看一个例子 QLabel * createLabel() {     QScopedPointer pLabel(new QLabel()); //  return pLabel.data();  //invalid 通过data()返回过后就被自动删除了,从而导致mian函数中的p1变成了野指针,程序崩溃     return  pLabel.take(); //valid }

int main(int argc, char *argv[]) {     QApplication a(argc, argv);     QScopedPointer p1(createLabel());     p1->setText("hello");     p1->show();

    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 i(newint[10]);     i[2] = 42;     …     return; // our integer array is now deleted using delete[] } 超出作用域过后会自动调用delete[]删除指针,这里就不展开描述了。

/************************************************************************************

最简单的写法: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 inst;

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 inst;

if (Q_UNLIKELY(!inst)) {

mutex.lock();

if (!inst) {

inst.reset(new T);

}

mutex.unlock();

}

return inst.data();

}

};

使用的时候直接这样——线程

1

MyClass* inst = Singleton::Instance();

除了用模板类,还能够利用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 inst; \

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 之单例模式

http://www.51969.com/

查看原文