c+11 单例模式

创建日期: 2024-06-03 16:36 | 作者: 风波 | 浏览次数: 14 | 分类: C++

来源:https://www.lance.moe/archives/post-356/

单例模式的特点

防拷贝构造

class noncopyable {
protected:
    noncopyable() = default;
    ~noncopyable() = default;
    noncopyable(noncopyable &&) = delete;                 // Move construct
    noncopyable(const noncopyable &) = delete;            // Copy construct
    noncopyable &operator=(const noncopyable &) = delete; // Copy assign
    noncopyable &operator=(noncopyable &&) = delete;      // Move assign
};

单例模式

template <typename T>
class singleton : public noncopyable {
public:
    static T &instance() {
        static T _instance {};
        return _instance;
    }
};

类内静态方法内的局部静态变量在 C++11 后可保证原子性和线程安全,所以只需要做防拷贝处理即可完成单例。将其制作成模板类,使用时只需要继承 singleton 即可。

实体类

class Client final : public singleton<Client> {
    friend class singleton<Client>;
protected:
    Client();
    ~Client();

public:
    static unsigned long version();
    static void set_version(unsigned long ver);
protected:
    unsigned long _version;
};

Client::Client() : _version(0) {
    // pass
}

Client::~Client() {
    // pass
}

unsigned long Client::version() {
    return instance()._version;
}

void Client::set_version(unsigned long ver) {
    instance()._version = ver;
}
  1. 一个单例模式类要加入 final 来限定不能被继承
  2. 要声明友元类,否则 singleton 基类无法获取实体类的构造函数

完整例子

#include <iostream>

class noncopyable {
protected:
    noncopyable() = default;
    ~noncopyable() = default;
    noncopyable(noncopyable &&) = delete;                 // Move construct
    noncopyable(const noncopyable &) = delete;            // Copy construct
    noncopyable &operator=(const noncopyable &) = delete; // Copy assign
    noncopyable &operator=(noncopyable &&) = delete;      // Move assign
};

template <typename T>
class singleton : public noncopyable {
public:
    static T &instance() {
        static T _instance;
        return _instance;
    }
};

class Client final : public singleton<Client> {
    friend class singleton<Client>;
protected:
    Client();
    ~Client();

public:
    static unsigned long version();
    static void set_version(unsigned long ver);
protected:
    unsigned long _version;
};

Client::Client() : _version(0) {
    // pass
}

Client::~Client() {
    // pass
}

unsigned long Client::version() {
    return instance()._version;
}

void Client::set_version(unsigned long ver) {
    instance()._version = ver;
}

int main() {
    using namespace std;
    Client::set_version(1919810ul);
    cout << Client::version() << endl; // 输出: 1919810
    const auto &client = Client::instance(); // 正确
    noncopyable test1();               // 编译器报错,noncopyable 没有构造器不能被实体化
    Client client();                   // 编译器报错,Client 构造器被保护不能被实体化
    Client *client = new Client();     // 编译器报错,同上
    return 0;
}

不改造类的情况下使用单例(技巧)

在实际工程中,可能修改一个类的成本比较大,或者是一个基类不方便被修改,那么可以用如下方法使用单例。(只是作为一个技巧,个人不是很推荐,因为无法像上面类继承方案一样做禁止构造操作。很可能出现很多手滑的误用。)

template <typename T>
T &use() {
    static T _instance {};
    return _instance;
}

这里我们利用模板来制作一个 use 函数。下面给一个用例:

#include <iostream>

template <typename T>
T &use() {
    static T _instance {};
    return _instance;
}

class Foo {
public:
    Foo() {
        puts("I was constructed!");
    }
    void bar() {
        puts("Called bar!");
    }
};

int main() {
    use<Foo>().bar();
    use<Foo>().bar();
    return 0;
}

总结

单例模式是一种很基础的设计方式,在面向对象编程流行的时期,单例模式广受批评。

第一点,主要是单例模式不基于接口,对继承、多态不友好。

第二点,在 C++11 普及之前,没有一种能够简单高效靠谱的实现单例模式的方案(大部分方案多多少少都存在一些问题)。

不过现在已经是各门语言里函数式语法满天飞的年代了,在很多 JavaScript 语言的库中,例如 react,编写视图甚至已经开始大力推广FC(Function Components)来代替年事已高的CC(Class Components),时间也证明过度面向对象、过度抽象对一个项目来说反而会降低代码可读性,有些时候不一定是一件好事。

我个人不反对单例,当然这并不意味着单例模式应该被滥用,还是要针对具体情况来定。

14 浏览
9 爬虫
0 评论