再议C++单例

457次阅读  |  发布于2年以前

前情提要

在之前的文章[C++的单例模式为什么不直接全部使用static,而是非要实例化一个对象?] 发布以后,陆陆续续收到很多网友留言提问,问题主要集中在文中的这一段:

class Singleton {
public:
    static void on() {Singleton::isOn = true;}
    static void off() {Singleton::isOn = false;}
    static bool state() {return Singleton::isOn;}
private:
    static bool isOn;
};

class Monitor: public Singleton {
public:
    static void addBrightness(int val) { brightness += val;}
    static void subBrightness(int val) { brightness -= val;}
    static int getBrightness() { return brightness;}

private:
    static int brightness;
};

如果有子类继承这一父类,来拓展成新的子类,比如Monitor显示器类有开关状态,同时扩展了一个亮度的成员。但是父子类的static成员变量是共享的,其isOn成员会有问题。

大家纷纷留言评论:“其isOn成员会有问题”,是指什么问题?会有什么问题?

甚至有很多网友是从其他公众号看到了我这篇文章,然后特地赶过来追问的。比如:

这位网友你是有多不想关注我……

进入正片

好了,我来解释一下。这上面的例子中isOn是父类Singleton中的静态成员,子类Monitor继承了父类后,isOn也还是同一个。如果这时候父类所代表的单例调用了on函数将isOn改为true。那么子类Monitor的isOn也会被修改,因为它们真的是同一个变量!不信你看:

单例继承写法1:

#include <iostream>
using namespace std;
class Singleton {
public:
    static void on() {Singleton::isOn = true;}
    static void off() {Singleton::isOn = false;}
    static bool state() {return Singleton::isOn;}
private:
    static bool isOn;
};
bool Singleton::isOn = true;

class Monitor: public Singleton {
public:
    static void addBrightness(int val) { brightness += val;}
    static void subBrightness(int val) { brightness -= val;}
    static int getBrightness() { return brightness;}

private:
    static int brightness;
};
int Monitor::brightness = 0;

int main() {
    Singleton::on();
    Monitor::on();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;

    Singleton::off();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;
}

输出:

Singleton state:1 Monitor state:1
Singleton state:0 Monitor state:0

但逻辑上两个类应该表示不同资源的“单例”,不应该互相影响才对。所以我说的有问题,不是说编译会有问题,而是说使用起来在逻辑上会有问题。有一种解法是你可以在子类里面再重新定义一个static的isOn变量及其相关的读写函数。这样父子类就解耦了。但是如果你要这样改的话,那么使用继承还有什么意义呢?完全没必要继承了啊。

好了,继续看。

单例继承写法2:

有的少年可能在此时会想起来,我上篇文章是在介绍Meyers' Singleton。于是想把isOn改成局部静态变量。那么这样是否就没问题呢?

#include <iostream>
using namespace std;
class Singleton {
public:
    static bool& getOn() {
        static bool isOn = true;
        return isOn;
    }
    static void on() {getOn() = true;}
    static void off() {getOn() = false;}
    static bool state() {return getOn();}
};

class Monitor: public Singleton {
public:
    static int& Brightness() {
        static int brightness = 0;
        return brightness;
    }
    static void addBrightness(int val) { Brightness() += val;}
    static void subBrightness(int val) { Brightness() -= val;}
    static int getBrightness() { return Brightness();}
};
int main() {
    Singleton::on();
    Monitor::on();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;

    Singleton::off();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;
}

同样有问题,虽然是局部静态变量了。但这种写法父子类使用的依旧是同一个isOn的静态变量!编译运行,输出还是:

Singleton state:1 Monitor state:1
Singleton state:0 Monitor state:0

单例继承写法3:

请注意并不是说使用了静态局部变量就是Meyers' Singleton。于是手快的少年,肯定立马写出了下一个版本:

#include <iostream>
using namespace std;
class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton inst;
        return inst;
    }
    static void on() {getInstance().isOn = true;}
    static void off() {getInstance().isOn = false;}
    static bool state() {return getInstance().isOn;}
protected:
    bool isOn = true;
};

class Monitor: public Singleton {
public:
    static Monitor& getInstance() {
        static Monitor inst;
        return inst;
    }
    static void addBrightness(int val) { getInstance().brightness += val;}
    static void subBrightness(int val) { getInstance().brightness -= val;}
    static int getBrightness() { return getInstance().brightness;}

private:
    int brightness = 0;
};
int main() {
    Singleton::on();
    Monitor::on();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;

    Singleton::off();
    cout<<"Singleton state:" << Singleton::state() 
        << " Monitor state:" << Monitor::state()<<endl;
}

少年你接近了真相,但是还是不对。这也不是Meyers' Singleton。输出还是:

Singleton state:1 Monitor state:1
Singleton state:0 Monitor state:0

单例继承写法4:

少年:“我悟了”

#include <iostream>
using namespace std;
class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton inst;
        return inst;
    }
    void on() {isOn = true;}
    void off() {isOn = false;}
    bool state() {return isOn;}
protected:
    bool isOn = true;
};

class Monitor: public Singleton {
public:
    static Monitor& getInstance() {
        static Monitor inst;
        return inst;
    }
    void addBrightness(int val) { brightness += val;}
    void subBrightness(int val) { brightness -= val;}
    int getBrightness() { return brightness;}

private:
    int brightness = 0;
};
int main() {
    Singleton::getInstance().on();
    Monitor::getInstance().on();
    cout<<"Singleton state:" << Singleton::getInstance().state()
        << " Monitor state:" << Monitor::getInstance().state()<<endl;

    Singleton::getInstance().off();
    cout<<"Singleton state:" << Singleton::getInstance().state()
        << " Monitor state:" << Monitor::getInstance().state()<<endl;
}

再次编译运行之后,输出:

Singleton state:1 Monitor state:1
Singleton state:0 Monitor state:1

没错这才是正确写法,要注意Meyers' Singleton,除了局部静态变量、数据成员是非静态这些条件以外,如果你要读写其中的数据成员,一定要像普通类一样写普通成员函数,而非静态成员函数来操作。如果你不希望写这些成员函数,那你就直接把单例中的数据成员声明成public吧……

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8