【问题标题】:Global const object shared between compilation units编译单元之间共享的全局 const 对象
【发布时间】:2013-12-21 06:11:03
【问题描述】:

当我声明并初始化一个 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 是非常量基本类型,你会得到什么!

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

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

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

我想知道为什么我在这个示例中没有收到 redfined 链接错误。

【问题讨论】:

    标签: c++ hyperlink global-variables constants


    【解决方案1】:

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

    命名空间范围内的常量变量具有内部链接。所以他们是 基本上是两个不同的变量。没有重新定义。

    3.5/3 [basic.link]:

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

    ——一个对象、引用、函数或函数模板,它是 显式声明为静态或,

    — 明确声明为 const 的对象或引用 显式声明的 extern 也没有先前声明为具有外部 连锁;或

    ——匿名联合的数据成员。

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


    如另一个答案中所述,头文件只是粘贴在 cpp 文件中。两个 cpp 文件中包含相同的头文件,但它们是独立的翻译单元。这意味着变量的一个实例与另一个实例不同。为了让编译器知道您已经在别处定义了变量,请使用 extern 关键字。这可确保在翻译单元之间仅共享一个实例。然而extern const Test test 只是一个声明。你需要一个定义。只要它在某个 cpp 文件中定义一次,在哪里定义它并不重要。可以随意声明多次(方便放在头文件中)

    例如:

    常数.h

    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();
    }
    

    常量.cpp

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

    生成文件

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

    【讨论】:

    • 关于“只要在某个 cpp 文件中定义一次,您在哪里定义它并不重要。”,这是(迂腐地)不正确的。首先,语言规范没有任何源文件的概念,也没有头文件和cpp的区别。其次,在实践中,有两种(C++11 三种)通用方法在头文件中定义这样的常量,没有任何一个定义规则问题。
    • 这能解决“初始化失败”吗?我可以从任何(其他)编译单元(例如Unit2.cpp)访问变量并确保它已被初始化(在Constant.cpp 内)吗?如果它不能解决初始化问题,那么 main 可能会随机打印 nullptrs ......
    【解决方案2】:

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

    // 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 变量的代码中直接获取幻数)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-22
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 2018-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多