【问题标题】:Correct way of initializing a class globally in C++在 C++ 中全局初始化类的正确方法
【发布时间】:2020-01-21 17:42:13
【问题描述】:

我知道全局是不好的,但作为一种实践,这是初始化在多个目标文件之间使用的全局类的正确方法吗?

标题 1.h

class test {
 int id;
 public:
 test(int in){
   id = in;
 }
 int getId(){
  return id;
 }
};

extern test t;

文件 1.cc

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

int main(){
 std::cout << t.getId() << std::endl;
 return 0;
}

文件 2.cc:

#include "1.h"

test t(5);

现在,如果我在标题中全局使用static 方法而不是extern,该怎么办?

如果我错了,请纠正我,但编译得很好,但是我会在目标文件和最终二进制文件中拥有相同 t 的 2 个不同的不相关副本?那不好吗?还是链接器会对其进行排序以消除多个副本?

【问题讨论】:

  • 当心static initialization order fiasco。还阅读了 ODR(一个定义规则)。另见isocpp.org/wiki/faq/ctors#static-init-order
  • 为什么头文件中有extern声明?
  • extern 总是在标题中。
  • @MosheRabaev 避免违反 ODR。
  • 你确定这是正确的吗?在你需要的地方使用 extern 更有意义,它更清楚,因为通过查看 main 我不知道 t 是在哪里定义的

标签: c++ static initialization one-definition-rule


【解决方案1】:

有全局实例,而不是全局类。

您拥有的是一个全局实例。是的,这听起来是对的,直到你得到多个相互依赖的全局实例。然后真正的乐趣就开始了。

【讨论】:

  • 我已将标题更正为initializing a class globally in C++
  • 但是如果你有两个全局实例呢?你不能初始化一个类,只能初始化某种类型的实例。
  • 我上面解释的静态方法会有问题吗?
  • @Josh 请关注、阅读并理解我对您的问题的评论中的链接 - 那么您不必问这个问题。
  • @JesperJuhl 是否建议尽可能避免使用static
【解决方案2】:

在全局级别将变量定义为“静态”意味着该变量将仅在编译单元(即“.o”文件)中定义,编译器不会导出符号。

换句话说:是的,会有多个变量同名但只对同一编译单元上的函数可见。

此外,“不可见”并不意味着“无法访问”。您仍然可以提供对变量的访问。例如:

1.h

struct Test { int value; };         // Class definition
Test& get_t();                      // Function declaration

1.cc

#include "1.h"
static Test t;                      // Variable declared as 'static'
Test& get_t() { return t; };

2.cc

#include "1.h"
#include <iostream>
int main()
{
  std::cout << get_t().value << std::endl;  // Static variable accessed
}

【讨论】:

  • 所以基本靠内存地址。但这似乎不是正确的方法,对吧? extern 似乎是声明全局变量的更好选择。
【解决方案3】:

我全局使用静态方法static test t;?

但是你的 test class 需要一个 int in 参数作为构造函数,所以你想要:

static test t(0); // or whatever int you want

【讨论】:

  • 我已经更正了,谢谢,但这很糟糕吗?它会创建多个副本吗?
  • 这是一条相关评论,但它回答了问题吗?
  • @Josh 它不会编译。
【解决方案4】:

如果您将标头中的extern 转换为static,您将在导入标头的每个编译单元中定义一个static 变量。所以不同cpp文件中的类将不再通过t“通信”,因为每个都有他们的。这很容易出错。

此外,在header 中添加static 的定义为an extremely bad practice。当某人包含一个标头时,人们不会期望它会创建变量。

t 声明为extern 是一种可接受的做法。但请注意,如果标头具有通用用途,则可能会降低其在其他项目中的可重用性。

您感兴趣的更多信息:

【讨论】:

    【解决方案5】:

    如果您将变量声明放在任何函数之外,则您将变量声明为“全局”。例如:

    1.cc

    int this_is_global;
    

    从这里开始,您可以在“1.cc”的任何函数中使用该变量。

    对于在任何其他文件中使用相同的变量,编译器需要知道它:

    2.cc

    extern int this_is_global;
    

    这里,关键字extern 告诉编译器该变量在其他地方声明,将查找它的任务交给链接器。

    如果你没有在此处添加extern关键字,编译器会将其视为一个新变量,链接器将有两个同名变量并会发出错误。您项目的所有源文件除了第一个都需要extern 关键字以避免重复符号。

    所以常见的做法是在包含文件中添加“extern”声明:

    1.cc

    int this_is_global;
    

    1.h

    extern int this_is_global;
    

    2.cc

    #include "1.h"
    

    另一方面,static 关键字告诉编译器不要导出符号。换句话说:变量将只存在于它声明的源文件中。您可以为每个源文件声明一次,并且会有不同的变量具有相同的名称。例如:

    1.h

    static int my_var;
    

    1.cc

    #include "1.h"
    

    2.cc

    #include "1.h"
    

    这样,您将拥有两个变量“my_var”,对其中任何一个的更改都不会影响另一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-17
      • 1970-01-01
      • 2022-10-20
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-01
      相关资源
      最近更新 更多