2013-12-21 40 views
4

当我声明并初始化一个const对象时。在编译单元之间共享全局const对象

// ConstClass.h 
class ConstClass 
{ 
}; 

const ConstClass g_Const; 

而两个cpp文件包含此标头。

// Unit1.cpp 
#include "ConstClass.h" 
#include "stdio.h" 

void PrintInUnit1() 
{ 
    printf("g_Const in Unit1 is %d.\r\n", &g_Const); 
} 

// Unit2.cpp 
#include "ConstClass.h" 
#include "stdio.h" 

void PrintInUnit2() 
{ 
    printf("g_Const in Unit2 is %d.\r\n", &g_Const); 
} 

当我构建解决方案,没有链接错误,你会得到什么。如果g_Const是一个非const根本的类型!

PrintInUnit1()和PrintInUnit2()显示在两个编译单元中有两个独立的具有不同地址的“g_Const”,为什么?

==============

我知道如何解决它。(使用extern关键字来声明,并且在一个CPP文件中定义它。)

我想知道为什么我没有在这个示例中找到redfin链接错误。在命名空间范围

回答

7

https://stackoverflow.com/a/6173889/1508519

常量变量具有内部连接。所以他们基本上是两个不同的变量。没有重新定义。

3.5/3 [basic.link]:

的名称具有命名空间范围(3.3.5)具有内部连接,如果它是 的

名称 - 一个对象,参考,功能或功能模板是 显式声明静态或,

- 一个对象或参考被明确地声明为常量并且既不 明确声明为extern也不先前声明为具有外部 联动;或

- 匿名工会的数据成员。

如果您希望它具有外部链接,请使用extern


正如其他答案中所述,头文件只是粘贴在cpp文件中。两个cpp文件都包含相同的头文件,但它们是分开的转换单元。这意味着变量的一个实例与另一个实例不同。在其他情况下,让编译器知道您在其他地方定义了变量,请使用extern关键字。这确保只有一个实例在翻译单元之间共享。但extern const Test test只是一个声明。你需要一个定义。只要在某个cpp文件中定义了一次,就无关紧要。您可以多次,你想要所以它声明

例如(这是方便把它放在一个头文件。):

常数。ħ

class Test 
{ 
}; 

extern const Test test; 

Unit1.cpp

#include "Constant.h" 
#include <iostream> 

void print_one() 
{ std::cout << &test << std::endl; } 

Unit2.cpp

#include "Constant.h" 
#include <iostream> 

void print_two() 
{ std::cout << &test << std::endl; } 

的main.cpp

extern void print_one(); 
extern void print_two(); 

int main() 
{ 
    print_one(); 
    print_two(); 
} 

Constant.cpp

#include "Constant.h" 
const Test test = Test(); 

的Makefile

.PHONY: all 
all: 
    g++ -std=c++11 -o test Constant.cpp Unit1.cpp Unit2.cpp main.cpp 
+1

“只要它在某个cpp文件中定义过一次,它就没有关系,只要它在某个cpp文件中定义了即可。”,这是(迂回)不正确的。首先,语言规范没有任何源文件的概念,并且头和cpp之间没有区别。其次,在实践中,有两个(使用C++ 11三种)常规方法在头中定义这样一个常量,没有任何一个定义规则问题。 –

+0

这是否解决了“初始化失败”?我可以从任何(其他)编译单元(例如'Unit2.cpp')访问变量,并确保它已被初始化(在Constant.cpp中)?如果它不能解决初始化问题,那么main可能会随机打印nullpt .... –

5

因为你把在头文件中的变量定义。包含头文件就像用文件的内容替换它一样。所以,第一个文件:

// Unit1.cpp 
#include "ConstClass.h" // this will be replace with the content of ConstClass.h 
#include "stdio.h" 

void PrintInUnit1() 
{ 
    printf("g_Const in Unit1 is %d.\r\n", &g_Const); 
} 

将成为(编译之前预处理阶段之后):

// Unit1.cpp 
// ConstClass.h 
class ConstClass 
{ 
}; 

const ConstClass g_Const; 
//this line is replaced with the content of "stdio.h" 

void PrintInUnit1() 
{ 
    printf("g_Const in Unit1 is %d.\r\n", &g_Const); 
} 

第二个文件是:

// Unit2.cpp 
// ConstClass.h 
class ConstClass 
{ 
}; 

const ConstClass g_Const; 
//this line is replaced with the content of "stdio.h" 

void PrintInUnit2() 
{ 
    printf("g_Const in Unit2 is %d.\r\n", &g_Const); 
} 

正如你所看到的,每个文件有单独的变量g_Const(这只是你的代码在这里的情况,可能根本就没有变量,就像宏一样,见我最后一段的解释)。

如果你想要的是不是变量只是在头文件中声明的定义,你应该使用extern关键字在头文件:

extern const ConstClass g_Const; 

然后,你可以把定义g_Const变量的ConstClass.c


有一些赶上在代码:

  • 在您的g_Const定义中没有指定恒定值,除非您需要默认值(0),否则您必须在定义中为其指定一个常数值。
  • 在printf里面,你取得C++的const变量的地址。这实际上迫使编译器在堆栈中创建变量。如果你没有使用该地址,它可能会推断出一个编译时间数字的行为类似于C语言中的宏(你可以直接在使用const变量的代码中获得幻数)。