浅谈__declspec(selectany)该何时用

__declspec是一个Microsoft Visual C++特定的编译器属性开关。括号中指明的是哪一个属性生效。关于__declspec的其他属性可以百度“__declspec msdn”查看微软的官方帮助。也可以参看博客中转载的文章。言归正传。__declspec(selectany)在MSDN中的说明是这样的:
    Tells the compiler that the declared global data item (variable or object) is a pick-any
    COMDAT (a packaged function). At link time, if multiple a definitions of COMDAT are
    seen, the linker picks one and discards the rest. Selectany can be used in initializing
    global data defined by headers,when the same header appears in more than one
    source file. 

简单翻译一下:告诉编译器定义的全局数据项(变量或对象)这是一套能被任意挑选的COMDAT(一套函数)。在链接时,如果多个COMDAT的定义被找到,链接器将挑选一个并剔除其他的多余的。Selectany可以被用于当定义有初始化全局变量数据的头文件被应用于多于一个的源文件时。我这样的翻译还是挺虚的,直白了说:当在头文件定义全局变量,并且这个头文件被include多次时可以用这个开关剔除由于多次include而产生的重定义。
看起来这个开关很有用,但这个开关我们用得并不多。很明显,根据msdn的解释,对于定义常量这个开关是用不上的了,而全局变量一般来说定义在cpp中,那么这个开关有什么用呢?
这正是这篇文章要讨论的主题。
(1)基于程序结构的整齐,统一将全局变量定义在一个全部cpp都引用的头文件,这样也就避免了在每个cpp中用extren导入外部变量。当然,这随你的习惯了。
(2)模板的设计。类模板,函数模板,这些肯定不会只被一个cpp所使用,按照一般的习惯都是写在头文件的。但是对于模板,其实现是在编译期完成的,那就要求必须
在编译的时候同时找到模板的定义和实现,也就是意味着不可能像以前的习惯一样,将类的声明放在头文件,将实现放在cpp中。那么模板类的静态成员变量怎么办呢?
类的静态成员必须在类外部初始化,如果是全写在头文件,当头文件include多于一次的时候就会出现类的静态变量重定义的问题,可以做一个简单的实验:

sy.h:
    class A  
    {  
    public:  
        static int u;  
    };  
    int A::u=1;  

1.cpp:
    #include"sy.h"  
      
    int main()  
    {  
        return 0;  
    }  

2.cpp:
    #include"sy.h" 

就这样三个文件,在一个工程中,组建一下(编译没问题,问题出在链接的时候),就会出现:
2.obj : error LNK2005: "public: static int A::u" (?u@A@@2HA) already defined in sy.obj
Debug/sy.exe : fatal error LNK1169: one or more multiply defined symbols found
这时候就只能使用__declspec(selectany)去解决了,将sy.h的第六行改为:
  __declspec(selectany) int A::u=1;  

即可解决问题。
下面来讨论一个问题,那么看下面的代码:

key.h:
    const char *Function[28]=  
    {  
        "menu","exit","rad",  
        "sin","cos","tan",  
        "sec","csc","cot",  
        "asin","acos","atan",  
        "acsc","asec","acot",  
        "sh","ch","th","ash",  
        "ach","ath","log",  
        "lg","ln","sum",  
        "mul","A","C"  
    };
这个头文件被include了多次。我的原意的是希望建立一个字符串的数组常量上面说了定义常量并不需要用这个开关,但为什么还是会出现重定义的问题呢?而且确实能用__declspec(selectany)去解决。
实际上这个可以不用__declspec(selectany)去解决,写成这样即可:
    const char* const Function[28]=  
    {  
        "menu","exit","rad",  
        "sin","cos","tan",  
        "sec","csc","cot",  
        "asin","acos","atan",  
        "acsc","asec","acot",  
        "sh","ch","th","ash",  
        "ach","ath","log",  
        "lg","ln","sum",  
        "mul","A","C"  
    };

请想一下用__declspec(selectany)去解决虽然没有问题,但符合我的原意吗?这样Function的内容到底是变量还是常量?

你可能感兴趣的:(浅谈__declspec(selectany)该何时用)