从零开始学C++之对象的使用(三):static 与单例模式、auto_ptr与单例模式、const 用法小结、mutable修饰符

一、static 与单例模式

单例模式也就是简单的一种设计模式,它需要:

保证一个类只有一个实例,并提供一个全局访问点 禁止拷贝

#include <iostream>
using namespace std;

class Singleton
{
public:
    static Singleton *GetInstance()
    {
        if (instance_ == NULL)
        {
            instance_ = new Singleton;
        }
        return instance_;
    }

    ~Singleton()
    {
        cout << "~Singleton ..." << endl;
    }
private:
    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);
    Singleton()
    {
        cout << "Singleton ..." << endl;
    }
    static Singleton *instance_;
};

Singleton *Singleton::instance_;

int main(void)
{
    //Singleton s1;
    //Singleton s2;

    Singleton *s1 = Singleton::GetInstance();
    Singleton *s2 = Singleton::GetInstance();

    //Singleton s3(*s1);        // 调用拷贝构造函数

    return 0;
}

上述程序虽然调用了两个GetInstance函数,但只调用一次构造函数,即创建一个对象。将赋值运算符和拷贝构造函数声明为私有,禁止拷贝。但程序存在一个问题就是对象生存期到时不会被析构。

为了解决对象不会被析构的问题,可以使用一个静态的嵌套类对象来解决:

#include <iostream>
using namespace std;

class Singleton
{
public:
    static Singleton *GetInstance()
    {
        if (instance_ == NULL)
        {
            instance_ = new Singleton;
        }
        return instance_;
    }

    ~Singleton()
    {
        cout << "~Singleton ..." << endl;
    }

    //static void Free()
    //{
    //  if (instance_ != NULL)
    //  {
    //      delete instance_;
    //  }
    //}

    class Garbo
    {
    public:
        ~Garbo()
        {
            if (Singleton::instance_ != NULL)
            {
                delete instance_;
            }
        }
    };
private:
    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);
    Singleton()
    {
        cout << "Singleton ..." << endl;
    }
    static Singleton *instance_;

    static Garbo garbo_;    // 利用对象的确定性析构
};

Singleton::Garbo Singleton::garbo_;
Singleton *Singleton::instance_;

int main(void)
{
    //Singleton s1;
    //Singleton s2;

    Singleton *s1 = Singleton::GetInstance();
    Singleton *s2 = Singleton::GetInstance();

    //Singleton s3(*s1);        // 调用拷贝构造函数


    return 0;
}

利用静态嵌套对象的确定性析构会调用Garbo类的析构函数,在析构函数内delete 单例类的指针。

上面办法比较繁琐,也可以返回局部静态对象的引用来解决:

#include <iostream>
using namespace std;

class Singleton
{
public:
    static Singleton &GetInstance()
    {
        static Singleton instance;      // 局部静态对象
        return instance;
    }

    ~Singleton()
    {
        cout << "~Singleton ..." << endl;
    }

private:
    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);
    Singleton()
    {
        cout << "Singleton ..." << endl;
    }
};

int main(void)
{
    Singleton &s1 = Singleton::GetInstance();
    Singleton &s2 = Singleton::GetInstance();



    return 0;
}

局部静态对象只会初始化一次,所以调用多次GetInstance函数得到的是同一个对象。由于函数内使用了静态对象,故不是线程安全的。实际上也可以使用auto_ptr 智能指针 来解决,程序如下,更详细的对auto_ptr 的讨论参见这里

#include <iostream>
#include<memory>
using namespace std;

class Singleton
{
public:
    static Singleton *GetInstance()
    {
        if (instance_.get() == NULL)
        {
            instance_ = auto_ptr<Singleton>(new Singleton);
        }
        return instance_.get();
    }

    ~Singleton()
    {
        cout << "~Singleton ..." << endl;
    }
private:
    Singleton(const Singleton &other);
    Singleton &operator=(const Singleton &other);
    Singleton()
    {
        cout << "Singleton ..." << endl;
    }
    static auto_ptr<Singleton> instance_;
};

auto_ptr<Singleton> Singleton::instance_;

int main(void)
{
    //Singleton s1;
    //Singleton s2;

    Singleton *s1 = Singleton::GetInstance();
    Singleton *s2 = Singleton::GetInstance();

    //Singleton s3(*s1);        // 调用拷贝构造函数

    return 0;
}

实际上,上述所有的单例模式例子都不是线程安全的,设想如果两个线程同时运行到语句if (instance == null),而此时该实例的确没有创建,那么两个线程都会创建一个实例。如果不希望加锁实现线程安全,可以使用饿汉模式(即在main函数之前先生成一个实例):

或者通过加锁方式实现,请参考这里

二、const成员函数、const 对象、mutable修饰符

(一)、const 成员函数

const成员函数不会修改对象的状态 const成员函数只能访问数据成员的值,而不能修改它

(二)、const 对象

如果把一个对象指定为const,就是告诉编译器不要修改它 const对象的定义:

const 类名 对象名(参数表);

const对象不能调用非const成员函数

用mutable修饰的数据成员即使在const对象或在const成员函数中都可以被修改。

#include <iostream>
using namespace std;

class Test
{
public:
    Test(int x) : x_(x), outputTimes_(0)
    {

    }
    int GetX() const
    {
        cout << "const GetX ..." << endl;
        //x_ = 100;
        return x_;
    }

    int GetX()
    {
        cout << "GetX ..." << endl;
        return x_;
    }

    void Output() const
    {
        cout << "x=" << x_ << endl;
        outputTimes_++;
    }

    int GetOutputTimes() const
    {
        return outputTimes_;
    }
private:
    int x_;

    mutable int outputTimes_;
};

int main(void)
{
    const Test t(10);
    t.GetX();

    Test t2(20);
    t2.GetX();

    t.Output();
    t.Output();
    cout << t.GetOutputTimes() << endl;
    return 0;
}

三、const 用法总结

可以对const 的用法做个小总结:

参考:

C++ primer 第四版 Effective C++ 3rd C++编程规范

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jeremy的技术点滴

实例变量的懒初始化

2994
来自专栏栗霖积跬步之旅

单例模式

定义:   单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象...

1869
来自专栏Python

Python中的单例模式的几种实现方式的及优化

单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只...

2266
来自专栏云霄雨霁

设计模式----单例模式

1350
来自专栏服务端技术杂谈

从单例模式说起

单例模式是我们比较常用的设计模式,玩好单例模式也会涉及到很多java基础知识。 单例作为全局性实例,在多线程情况下全局共享的变量会变得非常危险。

762
来自专栏xingoo, 一个梦想做发明家的程序员

Java几种单例模式的实现与利弊

多线程环境下无法保证单例效果,会多次执行 instance=new Singleton(),需要考虑到多线程

1262
来自专栏大大的微笑

设计模式之单例模式深究

为什么使用单例模式?         我认为是由于某些对象的创建比较耗时,对内存的消耗又比较大回收成本过高,这时候如果允许 不断的创建对象代价势必太高,如果...

2046
来自专栏calmound

设计模式--单例模式Singleton

单例模式顾名思义整个程序下只有一个实例,例如一个国家只有一个皇帝,一个军队只有一个将军。 单例模式的书写又分为饿汉模式和懒汉模式 饿汉模式    类中代码 pa...

2709
来自专栏武培轩的专栏

设计模式-单例模式

模式定义 确保一个类最多只有一个实例,并提供一个全局访问点。 单例模式分为饿汉式和懒汉式。 懒汉式单例模式:在类加载时不初始化。 饿汉式单例模式:在类加载时就完...

2744
来自专栏博客园

设计模式学习-单例模式

但是这么做不感觉有问题吗?假如这个类我们并不使用或在程序启动很久以后我们才使用,那么这个对象的预创建不就很浪费吗?并且如果这个对象的创建需要很大的资源,那......

523

扫码关注云+社区