【问题标题】:Initializing variables in header C++在头文件 C++ 中初始化变量
【发布时间】:2016-03-22 06:21:26
【问题描述】:

编辑:更正函数名称,并添加#pragma once

这是对我的问题的一个非常强大的简化,但如果我这样做:

啊.h

#pragma once
static int testNumber = 10;
void changeTestNumber();

A.cpp

#pragma once
#include "A.h"

void changeTestNumber()
{
    testNumber = 15;
} 

B.h

#pragma once
#include "A.h"
// some other stuff

B.cpp

#pragma once
#include "B.h"
// some other stuff

main.cpp

#pragma once
#include "B.h"
#include <iostream>

int main(){

changeTestNumber();
std::cout<<testNumber<<std::endl;

return 0;
}

为什么我在调用时没有得到 testNumber = 15? 当我使用包含在包含的标头的标头中的函数时,会发生什么? 如果我从 int testNumber 中删除 static,我会收到一些关于我的 testNumber 被初始化两次的错误。

那么当我这样做时,我的标头是否编译了两次?

提前致谢!

【问题讨论】:

  • 一个函数叫做changeNumber(你的原型),另一个叫做changeTestNumber(定义)
  • TL;DR:是的,有多个“testNumber”变量,每个标题包含一个。通常,开发人员会将静态文件放入 cpp 文件并使用“#pragma once”来控制标头定义其值的次数。有关“静态”的许多用途的更多信息可以在此站点上的其他软件中找到:stackoverflow.com/questions/15235526/…
  • 函数名错误是拼写错误,将编辑。我的“真实”代码中有一次#pragma,也应该把它放进去。谢谢你会检查那个链接!

标签: c++ header initialization


【解决方案1】:

除了明显不正确的命名(我认为这只是匆忙创建一个类似的示例而不是代码中的实际问题)之外,您需要在 .h/ 中将变量声明为 extern。 hpp 文件。你不能有一个extern 变量也是static,因为static 的(一个)用途是将变量包含在单个.cpp 文件中。

如果你改变:

static int testNumber = 10;

在您的A.h 文件中:

extern int testNumber;

然后在您的 A.cpp 文件中执行以下操作:

#include "A.h"
int testNumber = 10;

现在继续运行:

int main() {
    //changeNumber();
    std::cout << testNumber << std::endl; // prints 10
    changeTestNumber(); // changes to 15
    std::cout << testNumber << std::endl; // prints 15
    std::cin.ignore();
    return 0;
}

一定要修正函数名!

【讨论】:

  • 这里有很多好的答案,谢谢!这行得通:) 我现在在将它用于向量时遇到了一些麻烦,但是当我有时间开始询问之前,我会尝试更多。
【解决方案2】:

Goodies 和其他人当然是正确的,但让我再向前一步:

  1. static 使翻译单元的定义成为本地的。因此,在标头中定义静态全局变量将导致与其包含的翻译单元一样多的副本。除非那不是你想要的,否则不是这样的

  2. extern 告诉编译器全局变量存在于某处,但未定义,必须在链接阶段进行搜索。要使链接器成功,您需要在某个地方定义它(通常是一个更有意义的源文件)

  3. 省略这两个将导致“多重定义”链接器错误,其中多个源包含标头。

现在,第 2nd 案例有两个限制:

  • 即使在您提供模板库(或“仅标头”库)的情况下,它也会强制您使用可编译的源来实例化全局对象,从而根据需要使交付更加复杂
  • 它暴露了所谓的全局初始化惨败:如果您使用从其他地方定义的其他全局对象获取的值初始化一个全局对象,因为 C++ 不授予它们的构造顺序(最终属于链接器的工作方式),您可能无法正确初始化和销毁​​全局对象。

为了避免这一切,请考虑

  • 一个全局定义的函数,如果显式声明为inline 可以链接更多次,并且
  • 模板函数和类内定义的成员函数默认是内联的
  • 静态本地对象只创建一次,第一次遇到时

你可以在头文件中定义全局值,方法是让它们在函数中是静态的:比如 in

inline int& global_val() //note the &
{ static int z = 0; return z; }

唯一的缺点是您必须在每次访问时始终放置()

由于本地值是唯一的并且在调用时被实例化,这将确保,如果全局变量之间存在依赖关系(将int z=0 视为int z=something_else()),它们将按照需要的顺序创建并销毁逆序,即使在递归和多线程的情况下(c++14 起)

考虑到 C++ 向泛型和函数范式的演变,并考虑到将所有源代码放在一个编译单元中有时比链接多个源代码更可取...考虑一下不使用全局变量,而是用 内联instatiator函数。


大约 2 年后编辑:

C++17 终于引入了 inline 指令 for also for 变量声明,作为函数扩展的语法快捷方式。

所以-今天-你可以简单地写

inline const float PI = 3.14159;
inline int goodies = 25;

【讨论】:

  • 感谢有用的解释,不胜感激:)
【解决方案3】:

啊.h

extern int testNumber;
void changeNumber();

A.cpp

#include "A.h"

int testNumber = 10;

void changeTestNumber()
{
    testNumber = 15;
} 

B.h

#include "A.h"
// some other stuff

B.cpp

#include "B.h"
// some other stuff

main.cpp

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

int main(){

    changeTestNumber();
    std::cout<<testNumber<<std::endl;

    return 0;

}

请这样尝试。

【讨论】:

  • 这里有很多好的答案,谢谢!这行得通:) 我现在在将它用于向量时遇到了一些麻烦,但是当我有时间开始询问之前,我会尝试更多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
相关资源
最近更新 更多