C和C++的面向对象专题(7)——单例模式解决静态成员对象和全局对象的构造顺序难题

本专栏文章列表

一、何为面向对象

二、C语言也能实现面向对象

三、C++中的不优雅特性

四、解决封装,避免接口

五、合理使用模板,避免代码冗余

六、C++也能反射

七、单例模式解决静态成员对象和全局对象的构造顺序难题

八、更为高级的预处理器PHP

七、单例模式解决静态成员对象和全局对象的构造顺序难题

上回书说道,我们的程序有一个隐藏的漏洞,如果ClassRegister这个类所在的.o文件,如果在所有.o文件中是第一个被链接的的,那么就不会出问题。

这么说太抽象了,让我们画个图表

ClassRegister.o
--------------------
Meta.o
--------------------
Main.o

这样的结构,也就是链接顺序要这样指定

    gcc -o main ClassRegister.o Meta.o Main.o

就不会出问题,但如果调换一下顺序就可能出问题。

思考一下原因,ClassRegister中的map对象,是一个全局对象,而我们注册类的时候,也使用了全局对象的构造函数,两个谁先执行呢?这个就不得而知了,C++并未说明两个谁先谁后,而一般链接器,都是从前往后链接代码,而构造函数的执行顺序,也往往和链接时的顺序有关。

但这样实现就很不好,我们的系统居然要靠链接器的顺序才能正确编译执行,太不可靠了,万一用户没注意到这一点,直接编译链接,就会出现未知的错误。

那么如何避免这种情况呢?

C++的单例模式

C++中有一种极好的设计模式很适合这种情况,那就是用单例,单例模式也很容易理解,核心就是推迟构造,如果没有使用时,就不会被构造,被用到时,对象就会构造,并且仅一次,最常见的写法就是:

    class CSingleton
    {
    private:
        CSingleton()   //构造函数是私有的
        {
        }
        static CSingleton *m_pInstance;
    public:
        static CSingleton * GetInstance()
        {
            if(m_pInstance == NULL)  //判断是否第一次调用
                m_pInstance = new CSingleton();
            return m_pInstance;
        }
    };  

当然,我们这里并没有考虑多线程,因为多线程时单例模式一般要加锁来保障不会多次构造引发冲突。

于是经过简要修改,就能用单例模式设计一个类注册器了:

class ClassRegister
{
private:
    ClassRegister() { printf("register\n"); }  //构造函数是私有的
    std::map<const std::string, IMetaClass*> class_map;
public:
    static ClassRegister * GetInstance() {
        static ClassRegister instance;   //局部静态变量
        return &instance;
    }
    static void Add(const std::string s, IMetaClass* k) {
        GetInstance()->class_map[s] = k;
    }

    static IMetaClass* Get(const std::string s) {
        std::map<const std::string, IMetaClass*>& m =  GetInstance()->class_map;
        if (m.find(s) != m.end()) return m[s];
        else return NULL;
    }
};

这个类注册器简单实用,采用的设计方式和指针的模式稍有不同,这里用到了局部静态变量的概念。

局部静态变量能够改变对象的生存周期,这样就能很好的符合我们的要求。

时间: 2024-10-14 00:45:37

C和C++的面向对象专题(7)——单例模式解决静态成员对象和全局对象的构造顺序难题的相关文章

C和C++的面向对象专题(8)——更为高级的预处理器PHP

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 八.更为高级的预处理器PHP C++的宏在某些情况下非常难用,例如将代码展开成为这样: Macro( A, B, C, D ) => func("A", A); func("B", B); func("C&

C和C++的面向对象专题(1)——何为面向对象

题记: 面向对象是一种思想,而不是一门语言 我们上哪去找对象,都面向对象去了 本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 一.何为面向对象 现在学软件开发,都讲面向对象的编程模型,其实也很简单.用一句话来总结,面向对象就是将方法和方法的属性整合在一起,让每个方法引用的属性值尽可能在对象内部,对外

C和C++的面向对象专题(2)——C语言也能实现面向对象

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 二.C语言也能实现面向对象 今天要为大家介绍C语言的面向对象设计方法,正如题记上面所说,面向对象是一种思想,而并非是一种语言,我们将会介绍C语言实现的面向对象开发方式. 简单实用的C语言面向对象设计思路 众所周知,C++中的面向对象十分方便,但在C中,

C和C++的面向对象专题(3)——C++中的不优雅特性

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 三.C++中的不优雅特性 今天来说一说C++中不优雅的一些问题,C++虽然是面向对象的设计语言,但也有很多缺陷和弊病,我们将会讨论如何通过良好的设计解决这些问题. C++编译缓慢 C++编译慢已经成为了业界共识,一个大型C++项目甚至要用专用的服务器编

C和C++的面向对象专题(9)——Gtkmm的最佳实践

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 九.Gtkmm的最佳实践 九.Gtkmm的最佳实践 在跨平台的gui开发中,Qt一直是非常受欢迎的GUI开发框架,但Qt一个是依赖反射,需要特殊的预处理步骤,一个是库太过庞大,这就造成了一些不便的地方.今天介绍给大家的是Gtk库的C++绑定,Gtkmm

C和C++的面向对象专题(6)——C++也能反射

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 六.C++也能反射 今天我们来探讨C++的反射问题,缺乏反射机制一直是C++的大问题,很多系统的设计时,需要根据外部资源文件的定义,动态的调用内部的函数和接口,如果没有反射,将很难将外部的数据,转换为内部的方法. Java和.net的反射机制很容易实现

C和C++的面向对象专题(5)——合理使用模板,避免代码冗余

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 五.合理使用模板,避免代码冗余 下面我们来讨论一下,如何解决模板的不易封装的问题. 我们提供这样一种思路,对于链表一类的通用类型,我们尽量采取强制类型转换的方式,尽量避免模板的滥用. 同样,我们应该避免对结构体的直接存储,尽量使用类似java的指针传递

C和C++的面向对象专题(4)——解决封装,避免接口

本专栏文章列表 一.何为面向对象 二.C语言也能实现面向对象 三.C++中的不优雅特性 四.解决封装,避免接口 五.合理使用模板,避免代码冗余 六.C++也能反射 七.单例模式解决静态成员对象和全局对象的构造顺序难题 八.更为高级的预处理器PHP 四.解决封装,避免接口 恩,今天我们来讨论,如何通过设计,解决C++中的不优雅特性,改进项目的结构,改善编译速度. 上次我们提到,如果一个类的封装不好,容易导致种种不便,那么如何设计能够避免这种现象呢? class test { public: voi

单例模式,解决线程冲突

/** * 单例模式的使用 * * 单例创建用户服务对象 没必要给每个用户都创建一个 *  * 单例 :1 构造方法私有化 2 创建一个私有的静态变量 3 公共的静态方法 当做入口 *  * @param user */ /* * 第一种单例模式 */ // 问题 : 预先加载,没有手动实例化变量的时候 已经实例化了变量// 当你要使用对象的时候,只是把这个变量给你 // 1 构造方法私有化 外界不能直接访问private UserService(){}// 2 创建一个私有的静态变量(常量)p