代码之家  ›  专栏  ›  技术社区  ›  adf88

部分初始化其他模块中定义的变量

  •  3
  • adf88  · 技术社区  · 16 年前

    我正在考虑一个特定的解决方案,在这个解决方案中,我希望初始化在其他模块中定义的数组的单元格(将有许多模块初始化一个表)。在运行main之前不会读取数组(因此静态初始化顺序没有问题)。

    我的方法是:

    /* secondary module */
    
     extern int i[10]; // the array
    
     const struct Initialize {
      Initialize() { i[0] = 12345; }
     } init;
    


    /* main module */
    
     #include <stdio.h>
    
    
     int i[10];
    
     int main()
     {
      printf("%d\n", i[0]); // check if the value is initialized
     }
    

    编译器无法删除 init 常量,因为构造函数有副作用。我说的对吗?机构是否正常?在GCC(-O3)上,一切都很好。

    //编辑
    在现实世界中,会有许多模块。 我想避免一个额外的模块,一个收集所有次要初始化例程的中心位置。 (为了更好的可扩展性)。因此,每个模块触发自己的初始化非常重要。

    5 回复  |  直到 16 年前
        1
  •  3
  •   Sergey    16 年前

    这与MSVC编译器一起工作,但是GNU C++没有(至少对我来说)。GNU链接器将除去编译单元之外未使用的所有符号。我只知道一种保证这种初始化的方法——“init once”习惯用法。例如:

    一次。h:

    template <typename T>
    class InitOnce
    {
        T *instance;
        static unsigned refs;
    public:
        InitOnce() {
            if (!refs++) {
                instance = new T();
            }
        }
    
        ~InitOnce() {
            if (!--refs) {
                delete instance;
            }
        }
    };
    template <typename T> unsigned InitOnce<T>::refs(0);
    

    单位:H:

    #include "init_once.h"
    
    class Init : public InitOnce<Init>
    {
    public:
        Init();
        ~Init();
    };
    static Init module_init_;
    

    第二次:CPP:

     #include "unit.h"
     extern int i[10]; // the array
    
     Init::Init()
     {
         i[0] = 12345;
     }
     ...
    
        2
  •  0
  •   Brian Hooper    16 年前

    我觉得你不想 extern int i[10]; 但是,在您的主模块中,ADF88。

        3
  •  0
  •   Prasoon Saurav    16 年前

    编辑

     /*secondary module (secondary.cpp) */
    
      int i[10]; 
      void func()
      {
           i[0]=1;
    
      }
    

    .

     /*main module (main.cpp)*/
    
      #include<iostream>
    
      extern int i[];
      void func();
      int main()
      {
         func();
         std::cout<<i[0]; //prints 1
      }
    

    编译、链接和创建以及使用 g++ secondary.cpp main.cpp -o myfile

    一般来说,构造函数只用于(并且应该被使用)初始化类的成员。

        4
  •  0
  •   Ofek Shilon    16 年前

    这可能有效,但很危险。单个模块中的全局/静态构造顺序是未定义的,模块加载顺序也是未定义的(除非您明确管理它)。例如,假设在secondary.c initialize()ctor运行期间,已经存在i。您必须非常小心,不要让两个模块初始化相同的公共数据,或者让两个模块执行具有重叠副作用的初始化。

    我认为解决这种需求的更清洁的设计是让公共数据的所有者(您的主模块)将其公开为全局单例,并提供一个接口来执行所需的任何数据初始化。您有一个控制init顺序的中心位置,甚至可以控制并发访问(使用关键部分或其他并发原语)。按照您的简化示例,这可能是-

    / 主模块(MAIN.C) /

    包括: 类公用 { INTI;

    public:
        const int GetI()    { return i;}
        void SetI(int newI) { i = newI; }
        void incI()         
         {
          AcquireSomeLock();
          i++;
          ReleaseTheLock();
         }
    }
    
    CommonDat g_CommonDat;
    CommonDat*  getCommonDat()   { return &g_CommonDat; }
    
    int main(void)
    {
         printf("%d",getCommonDat()->GetI()); 
    }
    

    让二级模块在运行时的受控时间调用这些接口(和 不 在全球通行证期间)。

    (注:您将文件命名为C文件,但将问题标记为C++。建议的代码当然是C++。

        5
  •  0
  •   Matthieu M.    16 年前

    我可以问一下,当您可以使用 std::vector ?

    std::vector<int>& globalArray()
    {
      static std::vector<int> V;
      return V;
    }
    
    bool const push_back(std::vector<int>& vec, int v)
    {
      vec.push_back(v);
      return true; // dummy return for static init
    }
    

    此数组在第一次调用函数时被延迟初始化。

    您可以这样使用它:

    // module1.cpp
    static bool const dummy = push_back(globalArray(), 1);
    
    // module2.cpp
    static bool const dummy = push_back(globalArray(), 2);
    

    这看起来容易得多,也不容易出错。它不是多线程兼容,直到C++0x。