全局可变初始化顺序



全局变量的一个问题是,初始化顺序在翻译单元之间是不确定的,我们有一些惯例可以避免全局变量。但是我仍然想了解跨翻译单元的全球变量的初始化顺序,仅出于教育目的。

假设我们有这样的代码:

action_type.h

struct ActionType {
    static const ActionType addShard;  // struct static variables
}

action_type.cpp

ActionType ActionType::addShard(addShardValue); 

action_set.h

ActionSet(ActionType s);

my.cpp:

// global variables
ActionSet s(ActionType::addShard);

我的问题是:

  1. 我可以始终从全局" S"变量中获得确切的值?s取决于ActionType :: AddShard,该Shard在不同的翻译单元中定义。
  2. 如果不能保证,我该如何编译/链接/运行以获得错误的结果?我听说该顺序取决于链接阶段。

====让主题2讨论更容易,这是我的测试代码====

//  cat action.h 
#ifndef ACTION_H
#define ACTION_H
#include <iostream>
#include <bitset>
namespace m {
    class ActionSet {
    public:
        ActionSet();
        ActionSet(std::initializer_list<int> ids);
        void dump() const;
    private:
        std::bitset<4> _actions;
    };
}
#endif /* ACTION_H */
// action.cpp
#include "action.h"
#include <iostream>
namespace m {
ActionSet::ActionSet(): _actions(0) {
    std::cout << "from default" << std::endl;
}
ActionSet::ActionSet(std::initializer_list<int> ids) {
    std::cout << "from init list.." << std::endl;
    for(auto id : ids) {
        _actions.set(id, true);
    }
}
void ActionSet::dump() const {
    for(int i=0; i<4; i++) {
        std::cout << _actions[i] << ",";
    }
    std::cout << std::endl;
}
}
// const.h
#ifndef CONST_H
#define CONST_H
namespace m {
struct X {
    static int x;
    static int y;
};
}
#endif /* CONST_H */

//const.cpp

#include "const.h"
namespace m {
    int X::x = 0;
    int X::y = 2;
};
// f.h  
#ifndef F_H
#define F_H
#include "action.h"
#include <iostream>
namespace m {
 void f1();
void f2();
}
#endif /* F_H */
// f.cpp
#include "f.h"
#include "const.h"
namespace m {
    const ActionSet s{X::x, X::y};
    void f1() {
        s.dump();
    }

    void f2() {
        const ActionSet s2{X::x, X::y};
        s2.dump();
    }
};
// action.h 
#ifndef ACTION_H
#define ACTION_H
#include <iostream>
#include <bitset>
namespace m {
    class ActionSet {
    public:
        ActionSet();
        ActionSet(std::initializer_list<int> ids);
        void dump() const;
    private:
        std::bitset<4> _actions;
    };
}
#endif /* ACTION_H */
// action.cpp
#include "action.h"
#include <iostream>
namespace m {
ActionSet::ActionSet(): _actions(0) {
    std::cout << "from default" << std::endl;
}
ActionSet::ActionSet(std::initializer_list<int> ids) {
    std::cout << "from init list.." << std::endl;
    for(auto id : ids) {
        _actions.set(id, true);
    }
}
void ActionSet::dump() const {
    for(int i=0; i<4; i++) {
        std::cout << _actions[i] << ",";
    }
    std::cout << std::endl;
}
}
// main.cpp
#include "f.h"

int main(int argc, char *argv[])
{
    m::f1();
    m::f2();
    return 0;
}
// CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(project_name)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED on)
set(CMAKE_CXX_EXTENSIONS off)
set(CMAKE_EXPORT_COMPILE_COMMANDS on)
set( CMAKE_VERBOSE_MAKEFILE on )
add_executable(main const.cpp main.cpp f.cpp action.cpp)
add_executable(main2 main.cpp f.cpp action.cpp const.cpp)

您有很多代码可以通过,不幸的是,我无法找出ActionType实际上是什么。

正如您指出的那样,使用全局变量确实是一个坏主意。幸运的是,他们将constexpr添加到了语言中。使用constexpr,您可以创建在编译时"定义"的常数,而不会产生运行时影响。因此,无论您执行执行的顺序如何,都会产生正确的结果。

在不良方面, std::initializer_list ain ain ain ain ain constexpr(甚至在C 20中),std::bitset是。

#include <bitset>
struct ActionType {
    static constexpr std::bitset<4> addShard{0b0101};
};

使用上面的代码,您制作了一个constexpr变量,可以安全地用于初始化全局变量。同样,您可以将下一个类型创建为constexpr可用:

class ActionSet {
public:
    constexpr ActionSet();
    ActionSet(std::initializer_list<int> ids);
    constexpr ActionSet(std::bitset<4> actions) : _actions{actions} {}
    void dump() const;
private:
    std::bitset<4> _actions{0};
};
constexpr ActionSet s(ActionType::addShard);

简而言之,只要您能够在编译时"定义"所有内容(包括标题中的所有必需代码),就可以根据其他常数创建常数。可以在运行时进行调用常数方法。

从c 20开始,您应该能够写作:

[[constinit]] ActionSet s(ActionType::addShard);

这应该允许您在程序中使用非const方法。目前尚不清楚这是否仍然允许您在下一个constexpr变量的构造函数中使用" s"。

相关内容

  • 没有找到相关文章

最新更新