(我知道)在c ++中我可以 declare variable
超出范围,我无法运行任何代码/ statement,初始化全局/静态变量除外。
IDEA
使用棘手代码以下(例如)进行一些std::map
操作是不是一个好主意?
在这里,我使用void *fakeVar
并通过Fake::initializer()
对其进行初始化,并在其中执行任何操作!
std::map<std::string, int> myMap;
class Fake
{
public:
static void* initializer()
{
myMap["test"]=222;
// Do whatever with your global Variables
return NULL;
}
};
// myMap["Error"] = 111; => Error
// Fake::initializer(); => Error
void *fakeVar = Fake::initializer(); //=> OK
void main()
{
std::cout<<"Map size: " << myMap.size() << std::endl; // Show myMap has initialized correctly :)
}
答案 0 :(得分:14)
解决它的一种方法是使用一个带有构造函数的类来执行操作,然后声明该类的虚拟变量。像
struct Initializer
{
Initializer()
{
// Do pre-main initialization here
}
};
Initializer initializer;
你当然可以有多个这样的类进行杂项初始化。每个翻译单元中的顺序指定为自上而下,但未指定翻译单元之间的顺序。
答案 1 :(得分:11)
您不需要假类......您可以使用lambda初始化
auto myMap = []{
std::map<int, string> m;
m["test"] = 222;
return m;
}();
或者,如果它只是普通数据,请初始化地图:
std::map<std::string, int> myMap { { "test", 222 } };
答案 2 :(得分:6)
使用以下棘手的代码(例如)是一个好主意 做一些std :: map操作吗?
没有。
任何带有可变非局部变量的解决方案都是一个糟糕的想法。
答案 3 :(得分:5)
这是一个好主意吗??
不是真的。如果有人在他们的&#34;棘手的初始化中做出决定&#34;他们想要使用你的地图,但是在某个系统或其他系统上,或者在特定的重新链接之后,由于不明显的原因,你的地图在尝试使用后最终会被初始化?如果你让他们调用一个静态函数来返回对地图的引用,那么它可以在第一次调用时初始化它。使映射成为该函数内的静态局部变量,并在没有这种保护的情况下停止任何意外使用。
答案 4 :(得分:4)
§8.5.2陈述
除了使用constexpr说明符声明的对象之外 见7.1.5,变量定义中的初始化器可以包含 涉及文字和先前声明的任意表达式 变量和函数,无论变量的存储持续时间
因此,C ++标准完全允许您正在做的事情。也就是说,如果你需要执行“初始化操作”,最好只使用类构造函数(例如包装器)。
答案 5 :(得分:2)
您所做的是完全合法的C ++。所以,如果它对你有用,并且可以被任何使用该代码的人维护和理解,那就没问题了。尽管Joachim Pileborg的样本对我来说更清楚。
如果在初始化期间彼此使用,则可能会出现初始化此类全局变量的一个问题。在这种情况下,确保以正确的顺序初始化变量可能很棘手。出于这个原因,我更喜欢创建InitializeX,InitializeY等函数,并从Main函数中以正确的顺序显式调用它们。
错误的排序也可能在程序退出期间导致问题,其中全局变量仍然会在其中一些可能已被销毁时尝试互相使用。同样,在Main返回之前,一些显式的破坏以正确的顺序调用可以使它更清晰。
所以,如果它适合你,那就去吧,但要注意陷阱。同样的建议适用于C ++中的几乎所有功能!
你在问题中说过你自己认为代码很棘手&#39;没有必要为了它而使事情过于复杂。所以,如果你有一个看起来不那么棘手的替代方案。对你...可能会更好。
答案 6 :(得分:2)
当我听到&#34;棘手的代码&#34;时,我立即想到代码气味和维护噩梦。要回答你的问题,不,这不是一个好主意。虽然它是有效的C ++代码,但这是不好的做法。对于这个问题,还有其他更明确和有意义的替代方案。详细说明,你的初始值设定项()方法返回void * NULL的事实就你的程序的意图而言毫无意义(即你的代码的每一行应该有意义的目的),你现在有了另一个不必要的全局变量 fakeVar ,它不必要地指向NULL。
让我们考虑更少的&#34;棘手的&#34;备选方案:
如果您只有一个 myMap 的全局实例非常重要,那么使用 Singleton Pattern 会更合适,你可以在需要时懒洋洋地初始化 myMap 的内容。请记住,Singleton模式有自己的问题。
使用静态方法创建并返回地图或使用全局命名空间。例如,类似于以下内容:
// global.h
namespace Global
{
extern std::map<std::string, int> myMap;
};
// global.cpp
namespace Global
{
std::map<std::string, int> initMap()
{
std::map<std::string, int> map;
map["test"] = 222;
return map;
}
std::map<std::string, int> myMap = initMap();
};
// main.cpp
#include "global.h"
int main()
{
std::cout << Global::myMap.size() << std::endl;
return 0;
}
如果这是具有专业功能的地图,创建自己的类(最佳选项)!虽然这不是一个完整的例子,但你明白了这一点:
class MyMap
{
private:
std::map<std::string, int> map;
public:
MyMap()
{
map["test"] = 222;
}
void put(std::string key, int value)
{
map[key] = value;
}
unsigned int size() const
{
return map.size();
}
// Overload operator[] and create any other methods you need
// ...
};
MyMap myMap;
int main()
{
std::cout << myMap.size() << std::endl;
return 0;
}
答案 7 :(得分:1)
在C ++中,你不能在任何函数之外使用语句。但是,您声明了全局对象,并且在主要启动之前对这些全局对象的构造函数(初始化程序)调用是自动的。在你的例子中,fakeVar是一个通过类静态范围的函数初始化的全局指针,这绝对没问题 即使是全局对象也会提供全局对象构造函数执行所需的初始化。 例如,
class Fake
{
public:
Fake() {
myMap["test"]=222;
// Do whatever with your global Variables
}
};
Fake fake;
答案 8 :(得分:1)
这是一个统一构建(单翻译单元构建)可能非常强大的情况。 __COUNTER__
宏是C和C ++编译器中事实上的标准,有了它,你可以在全局范围内编写任意命令式代码:
// At the beginning of the file...
template <uint64_t N> void global_function() { global_function<N - 1>(); } // This default-case skips "gaps" in the specializations, in case __COUNTER__ is used for some other purpose.
template <> void global_function<__COUNTER__>() {} // This is the base case.
void run_global_functions();
#define global_n(N, ...) \
template <> void global_function<N>() { \
global_function<N - 1>(); /* Recurse and call the previous specialization */ \
__VA_ARGS__; /* Run the user code. */ \
}
#define global(...) global_n(__COUNTER__, __VA_ARGS__)
// ...
std::map<std::string, int> myMap;
global({
myMap["test"]=222;
// Do whatever with your global variables
})
global(myMap["Error"] = 111);
int main() {
run_global_functions();
std::cout << "Map size: " << myMap.size() << std::endl; // Show myMap has initialized correctly :)
}
global(std::cout << "This will be the last global code run before main!");
// ...At the end of the file
void run_global_functions() {
global_function<__COUNTER__ - 1>();
}
一旦您意识到可以使用它来初始化静态变量而不依赖于C运行时,这一点就非常强大。这意味着您可以生成非常小的可执行文件,而无需避开非零全局变量:
// At the beginning of the file...
extern bool has_static_init;
#define default_construct(x) x{}; global(if (!has_static_init()) new (&x) decltype(x){})
// Or if you don't want placement new:
// #define default_construct(x) x{}; global(if (!has_static_init()) x = decltype(x){})
class Complicated {
int x = 42;
Complicated() { std::cout << "Constructor!"; }
}
Complicated default_construct(my_complicated_instance); // Will be zero-initialized if the CRT is not linked into the program.
int main() {
run_global_functions();
}
// ...At the end of the file
static bool get_static_init() {
volatile bool result = true; // This function can't be inlined, so the CRT *must* run it.
return result;
}
has_static_init = get_static_init(); // Will stay zero without CRT
答案 9 :(得分:1)
此答案与Some programmer dude's answer类似,但可能会更简洁一些。从C ++ 17(添加std::invoke()
开始),您可以执行以下操作:
#include <functional>
auto initializer = std::invoke([]() {
// Do initialization here...
// The following return statement is arbitrary. Without something like it,
// the auto will resolve to void, which will not compile:
return true;
});