MFC静态库中的全局变量初始化的问题

fliar 2013-06-06 11:06:22
有一个问题困扰了我很久,就是静态库中的全局变量的初始化的问题,大家也可能都遇到过,如果没有显式引用到全局变量的cpp,这个全局变量就不会被链接,导致一些依赖于这些变量的自动执行过程没有办法完成。
最近看了看侯捷的深入浅出mfc,感觉mfc似乎可能解决了这个问题。
侯捷的简化版RTTI最终也是基于全局变量的,这个编他的例子不会遇到问题,但是放到静态库那个链表一定是空的:)
然后去看了看mfc的实现,发现mfc其实把那部分移到了IMPLEMENT_SERIAL,不知道是不是版本的关系与侯捷的讲法不同。
随便建了个项目用mfc静态库下个断点看看,发现链表的建立其实还是在初始化全局变量的时候做。然后搜了下IMPLEMENT_SERIAL,发现好像除了一两个有写IMPLEMENT_SERIAL的地方没加进表(不完全确定有没有看错),其他都加进去了。

不知道mfc是怎么实现这些全局变量的链接的?

写了这么多不知道大家看懂了没orz
...全文
353 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2013-06-21
  • 打赏
  • 举报
回复
全局变量,集中定义就可以了,完全不用全局变量,也是不可能的。
pkrobbie 2013-06-07
  • 打赏
  • 举报
回复
不要花这么多时间研究全局变量了。全局变量在任何真正的软件项目里面都是定时炸弹。好的代码里面不会用这个东西。
wocow3 2013-06-07
  • 打赏
  • 举报
回复
这怎么能算显示引用呢?应用程序还是只用T6Module,而InitA其实并没有被用到,完全可以去掉,但就是因为他与T6Module在一个cpp,所以他的静态量也被构造 也就是说,要想让一个静态库中一个obj对应的cpp的所有静态量得到构造,一个办法就是引用这个cpp中的任意一个符号 ,这样这个cpp中的所有静态量都会得到构造,这是lib的obj initseg段被合并的结果 而你用MFC,肯定会间接的引用各种类的各种符号,所以每个类的静态量得到构造,表就创建起来了
fliar 2013-06-07
  • 打赏
  • 举报
回复
呵呵,改例子那就不好说了,把他移到T6Module.cpp下肯定可以没错,我认为这算是显式引用吧,因为这个cpp肯定会用到。 我也可以把全局变量移到头文件,然后main的项目去引用头文件,那样也没问题,这应该也是显示引用。 一般用全局变量都是为了在不修改已有文件的情况下神不知鬼不觉地达成某些目的,比如注册什么之类。 可是静态库,我还没有找到这种方法。 所以回到原点,MFC是如何用静态库做到这种事的
wocow3 2013-06-07
  • 打赏
  • 举报
回复
[quo[i]te=引用 13 楼 fliar 的回复:] 楼上,你可以按我给的代码建两个项目试一下,看看i = 1还是99 没有在外部显式引用的静态库cpp中的全局变量绝对不会被链接,静态库链接应该就是这么设计的[/quote] 这个问题是lib与obj的区别 lib是一堆obj的集合,linker对lib的引用是只搜索和引用感兴趣的“部分”,没有被直接调用或者间接调用的的“部分”不会进入EXE。而独立的obj是照单全收全部进入exe。 但是,linker对lib的解析不是基于函数符号的,这个“部分”是以lib中obj为单位,如果你把init.cpp的与T6Module.cpp合并成一个文件,你试下,答案可能非你所想。 因为这里你是分离的两个cpp,虽然在一个lib中,直接引用T6Module只会引用T6Module.obj,而InitA在init.obj中,不会被包含,构造函数不会被调用。 但如果是一个cpp,引用T6Module的结果会造成InitA被捎带,结果会出现99 貌似偏题了。
asdjy123 2013-06-07
  • 打赏
  • 举报
回复
mark,回去继续看
fliar 2013-06-07
  • 打赏
  • 举报
回复
楼上,你可以按我给的代码建两个项目试一下,看看i = 1还是99 没有在外部显式引用的静态库cpp中的全局变量绝对不会被链接,静态库链接应该就是这么设计的
fliar 2013-06-06
  • 打赏
  • 举报
回复
呃,不太理解链接位置往后靠的意思。 我的意思是说,这些全局变量其实只是为了在main之前注册一些东西,对于静态库的使用者来说应该是不需要引用。 也就是说这些全局变量只作用于静态库中,但是如果链接时,如果链接器认为静态库某段没用到,就不会链接,全局变量就不会在main之前执行初始化
无言猪 2013-06-06
  • 打赏
  • 举报
回复
既然有"导致一些依赖于这些变量的自动执行过程没有办法完成。",自动执行过程不就引用到这个全局变量了吗.你把lib的链接位置往后靠,当链接器发现只有引用而没有定义的全局符号时,会记录下来,到后面的lib或者cpp中查找,如果lib在前面,连接器查找记录时没有发现这个全局变量,就会忽略,然后到后面再发现这个全局变量,已经错过链接进来的机会了.
wocow3 2013-06-06
  • 打赏
  • 举报
回复
引用 10 楼 fliar 的回复:
分析啦,分析的结果就是,他还是用全局变量初始化来自动执行,可是为啥他在静态库里的全局变量也会初始化orz
这个跟全局变量在不在静态库没有关系,所有的静态量都是在进入mainstartup之后用户main之前初始化的。 链接的时候,linker收集所有的静态量,把需要调用构造函数的静态量都在一个特殊的段中的登记一下,包括静态库中被应用的。 exe执行的会后,先调用登记在册的静态量的构造,再进入main。回到MFC,进入winmain前,MFC的RTTI网就已经好了 这本来就是C++静态成员量先于main构造的机制。
无言猪 2013-06-06
  • 打赏
  • 举报
回复
MFC一般是用类的静态成员变量,这个类一般会被你所用的类继承,自然就被初始化.
fliar 2013-06-06
  • 打赏
  • 举报
回复
分析啦,分析的结果就是,他还是用全局变量初始化来自动执行,可是为啥他在静态库里的全局变量也会初始化orz
无言猪 2013-06-06
  • 打赏
  • 举报
回复
MFC的宏预编译了一看就明白了,一种是通过继承关系被引用的父类,一种就是你自己定义的支持Serial的类.通过一个静态或者全局结构的构造函数来进行链表的链接.把DECLARE和IMPLEMENT宏展开了分析一下就能得出结果了.我这也是以前看过,具体的有些出入.
fliar 2013-06-06
  • 打赏
  • 举报
回复
这个我知道,我只是想了解下mfc里面那个链表是不是用了什么神奇的技巧来实现初始化的
无言猪 2013-06-06
  • 打赏
  • 举报
回复
这个你应该封装到一个init函数里面去做,保证init能被引用,或者直接显式的提供一个init函数,libc里面也有全局变量,crtmain还不是得去初始化,想自动初始化,要么就和Saleayas说的一样,要么就显式的去初始化,或者把初始化的代码放在必然会被引用的.o文件所依赖的.c中.
fliar 2013-06-06
  • 打赏
  • 举报
回复
呃,如果用一两个选项就可以强制链接所有cpp,那我可以接受,这是只有一个全局变量的情况,如果一堆的话总不能每个都强制链接一下(话说要怎么链接orz)。 而且对于使用静态库的人来说,理论上它不需要知道这个全局变量的存在。
Saleayas 2013-06-06
  • 打赏
  • 举报
回复
在你的项目里面强制连接 g_a;
fliar 2013-06-06
  • 打赏
  • 举报
回复
那来个简单的例子吧,静态库里有三个文件: T6Module.h

class T6Module
{
public:
	T6Module();
	virtual ~T6Module();
public:
	static T6Module * getModule ( void );

	void setIntValue ( int IntValue );
	int getIntValue() const;
private:
	int m_IntValue;
};
T6Module.cpp

#include "t6module.h"

T6Module::T6Module()
: m_IntValue(1)
{
}

T6Module::~T6Module()
{
}

static T6Module s_T6Module;

T6Module* T6Module::getModule ( void )
{
	return &s_T6Module;
}

void T6Module::setIntValue ( int IntValue )
{
	this->m_IntValue = IntValue;
}

int T6Module::getIntValue() const
{
	return m_IntValue;
}
init.cpp

#include "t6module.h"

class InitA
{
public:
	InitA(void)
	{
		T6Module::getModule()->setIntValue(99);
	};
};

InitA g_a;
使用静态库的程序:

#include <stdio.h>
#include "../T006_PragmaDataSeg_SLib/t6module.h"

int main(int argc, char **argv)
{
	printf("hello world\n");
	int i = T6Module::getModule()->getIntValue();
	return 0;
}
如果不把前面三个包成静态库,全放在一个项目下,init.cpp就会被链接,那么main里面 i = 99; 如果包成静态库,init.cpp就不会被链接,因为链接器认为这个档没有用到。那么i = 1; 我现在的推测是mfc其实内部有引用到他写implement_serial的东西,所以链接器还是把那些档链接进去了
无言猪 2013-06-06
  • 打赏
  • 举报
回复
引用 2 楼 fliar 的回复:
呃,不太理解链接位置往后靠的意思。 我的意思是说,这些全局变量其实只是为了在main之前注册一些东西,对于静态库的使用者来说应该是不需要引用。 也就是说这些全局变量只作用于静态库中,但是如果链接时,如果链接器认为静态库某段没用到,就不会链接,全局变量就不会在main之前执行初始化
最后一句是没问题的,但是逻辑上有问题,main之前注册会用到全局变量,那就是在lib之外有地方引用到了这个全局变量.但是你又说没用到,不链接进来.举个稍微具体点的例子吧,方便大家分析.

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧