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

一、static 与单例模式

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

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

禁止拷贝

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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函数,但只调用一次构造函数,即创建一个对象。将赋值运算符和拷贝构造函数声明为私有,禁止拷贝。但程序存在一个问题就是对象生存期到时不会被析构。


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

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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 (insta n ce_ != NULL)
     //  {
     //      delete insta n ce_;
     //  }
     //}

     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 单例类的指针。


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

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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 的讨论参见这里。


 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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函数之前先生成一个实例):

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


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


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

(一)、const 成员函数

const成员函数不会修改对象的状态

const成员函数只能访问数据成员的值,而不能修改它

(二)、const 对象

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

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

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


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


 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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;
}

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


三、const 用法总结

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

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


参考:

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


你可能感兴趣的:(const对象,const成员函数,static与单例模式,mutable修饰符)