是否适合在头文件中将值设置为"const char*"

sud*_*sud 15 c++ const-char effective-c++

我见过人们使用2种方法来声明和定义char *.

Medhod 1:头文件如下

extern const char* COUNTRY_NAME_USA = "USA";
Run Code Online (Sandbox Code Playgroud)

Medhod 2:
头文件具有以下声明:

extern const char* COUNTRY_NAME_USA;
Run Code Online (Sandbox Code Playgroud)

cpp文件具有以下定义:

extern const char* COUNTRY_NAME_USA = "USA";
Run Code Online (Sandbox Code Playgroud)
  1. 方法1在某种程度上是错误的吗?
  2. 两者有什么区别 ?
  3. 我理解" const char * const var"和" const char * var" 之间的区别.如果在上面的方法中,如果const char * const var在方法1中声明和定义标题中的" "是否有意义?

AnT*_*AnT 28

第一种方法确实是错误的,因为它在头文件中定义COUNTRY_NAME_USA具有外部链接的对象.一旦该头文件被包含到多个翻译单元中,就会违反一个定义规则(ODR).代码将无法编译(更确切地说,它将无法链接).

第二种方法是正确的.关键字extern在定义中是可选的,即在cpp文件中你可以做

const char* COUNTRY_NAME_USA = "USA"
Run Code Online (Sandbox Code Playgroud)

假设头文件中的声明在此翻译单元中位于此定义之前.

此外,我猜测,因为对象名称是大写的,它可能是一个常量.这样,那么它应该被声明/定义为const char* const COUNTRY_NAME_USA(注意额外的const).

最后,考虑到最后一个细节,您可以将常量定义为

const char* const COUNTRY_NAME_USA = "USA"; // no `extern`!
Run Code Online (Sandbox Code Playgroud)

在头文件中.由于它现在是常量,因此默认情况下它具有内部链接,这意味着即使头文件包含在多个转换单元中也不存在ODR违规.在这种情况下,您会COUNTRY_NAME_USA在每个翻译单元中获得一个单独的左值(在extern方法中,您将获得整个程序的左值).只有你知道你需要什么.

  • @sud:如果你将`COUNTRY_NAME_USA`声明为常量(最后一个例子),那么从技术上讲,每个翻译单元都会获得自己的`COUNTRY_NAME_USA`指针副本,但不一定是它自己的副本``USA"`literal.这取决于实现,但通常我会期望:1)整个程序只获得一个"USA"文字,2)`COUNTRY_NAME_USA`指针被优化掉(因为它是一个常量).所以,不应该有任何性能问题.实际上,总是可以在头文件中定义*small*常量(指针,整数等). (2认同)
  • 对于执行常量折叠的链接器(如果启用优化,大多数情况下都会这样做),只有字符串字符数据的一份副本最终会出现在最终的可执行文件中。 (2认同)

Ben*_*igt 10

重点是什么?

如果你想查找字符串(可以本地化),这将是最好的:

namespace CountryNames {
    const char* const US = "USA";
};
Run Code Online (Sandbox Code Playgroud)

由于指针是const,它现在具有内部链接,不会导致多个定义.大多数链接器也会组合冗余常量,因此您不会在可执行文件中浪费空间.

如果你想通过指针相等来比较字符串,上面的内容是不可移植的,因为如果链接器执行常量折叠优化,指针只会相等.在这种情况下,在头文件中声明一个extern指针是要走的路(如果你不打算重新定位它,它也应该是const).


Bil*_*ter 5

如果必须有全局变量,通常的做法是在.h文件中声明它们并在一个(且只有一个).cpp文件中定义它们.

在.h文件中;

extern int x;
Run Code Online (Sandbox Code Playgroud)

在.cpp文件中;

int x=3;
Run Code Online (Sandbox Code Playgroud)

我在你的例子中使用了int(最基本的基本类型?)而不是const char*,因为问题的本质不依赖于变量的类型.

基本的想法是你可以多次声明一个变量,所以包含.h文件的每个.cpp文件都会声明变量,这很好.但是你只定义一次.定义是指定变量初始值的语句(带有=).您不希望在.h文件中定义,因为如果多个.cpp文件包含.h文件,您将获得多个定义.如果您对一个变量有多个定义,则链接时会出现问题,因为链接器想要分配变量的地址,如果有多个副本,则无法合理地执行此操作.

后来添加的其他信息试图缓解Sud的混乱;

尝试将问题减少到最小的部分,以便更好地理解它;

想象一下,你有一个包含三个.cpp文件的程序.为了构建程序,每个.cpp被单独编译以创建三个目标文件,然后将三个目标文件链接在一起.如果三个.cpp文件如下(例A,良好的组织);

file1.cpp

extern int x;
Run Code Online (Sandbox Code Playgroud)

file2.cpp

extern int x;
Run Code Online (Sandbox Code Playgroud)

file3.cpp

extern int x;
Run Code Online (Sandbox Code Playgroud)

然后文件将编译并链接在一起没有问题(至少就变量x而言).没有问题,因为每个文件只声明变量x.声明只是声明我可能(或可能不)使用某个地方的变量.

实现同样目标的更好方法是以下(示例A,更好的组织);

header.h

extern int x;
Run Code Online (Sandbox Code Playgroud)

file1.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file2.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file3.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

这实际上是完全相同的,对于三个编译中的每一个,编译器看到与之前处理.cpp文件(或专家称之为翻译单元)的文本相同的文本,因为#include指令只是从另一个文件中提取文本.然而,这是对前面示例的改进,因为我们只将声明放在一个文件中,而不是放在多个文件中.

现在考虑另一个工作示例(例子B,良好的组织);

file1.cpp

extern int x;
Run Code Online (Sandbox Code Playgroud)

file2.cpp

extern int x;
Run Code Online (Sandbox Code Playgroud)

file3.cpp

extern int x;
int x=3;
Run Code Online (Sandbox Code Playgroud)

这也可以.所有三个.cpp文件都声明x,一个实际定义它.我们可以继续在操作变量x的三个文件中的任何一个函数中添加更多代码,我们不会得到任何错误.我们应该再次使用头文件,以便声明只进入一个物理文件(例子B,更好的组织).

header.h

extern int x;
Run Code Online (Sandbox Code Playgroud)

file1.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file2.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file3.cpp

#include "header.h"
int x=3;
Run Code Online (Sandbox Code Playgroud)

最后考虑一个不起作用的例子(例子C,不起作用);

file1.cpp

int x=3;
Run Code Online (Sandbox Code Playgroud)

file2.cpp

int x=3;
Run Code Online (Sandbox Code Playgroud)

file3.cpp

int x=3;
Run Code Online (Sandbox Code Playgroud)

每个文件都会编译没有问题.问题发生在链接时,因为现在我们已经定义了三个单独的int x变量.它们具有相同的名称,并且全局可见.链接器的工作是将单个程序所需的所有对象拉入一个可执行文件中.全局可见对象必须具有唯一名称,以便链接器可以将对象的单个副本放在可执行文件中的一个已定义地址(位置),并允许所有其他对象在该地址访问它.在这种情况下,链接器无法使用全局变量x来完成它的工作,因此会阻塞错误.

另外,给出不同的定义,不同的初始值不能解决问题.使用关键字static在每个定义之前确实解决了问题,因为现在变量不是全局可见的,而是在定义的.cpp文件中可见.

如果将全局变量定义放入头文件中,则没有任何必要的更改(例如,在这种情况下,标题组织没有帮助);

header.h

int x=3;  // Don't put this in a .h file, causes multiple definition link error
Run Code Online (Sandbox Code Playgroud)

file1.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file2.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

file3.cpp

#include "header.h"
Run Code Online (Sandbox Code Playgroud)

Phew,我希望有人读到这个并从中获益.有时,提问者急切地想要根据基本概念进行简单的解释,而不是先进的计算机科学家的解释.