运行C++;函数范围外的代码 < C++ >强> >(强)>强> >可以> >代码>声明变量超出范围,并且 > < < /强> >运行任何代码/语句,除了初始化全局/静态变量。< /P>
创意 为了(例如)进行一些运行C++;函数范围外的代码 < C++ >强> >(强)>强> >可以> >代码>声明变量超出范围,并且 > < < /强> >运行任何代码/语句,除了初始化全局/静态变量。< /P>,c++,static,initialization,initializer,C++,Static,Initialization,Initializer,创意 为了(例如)进行一些std::map操作,使用下面的复杂代码是一个好主意吗 在这里,我使用void*fakeVar并通过Fake::initializer()对其进行初始化,然后在其中执行任何操作 std::map<std::string, int> myMap; class Fake { public: static void* initializer() { myMap["test"]=222; // Do whatever
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 :)
}
std::map myMap;
类假
{
公众:
静态void*初始值设定项()
{
myMap[“测试”]=222;
//对全局变量执行任何操作
返回NULL;
}
};
//myMap[“错误”]=111;=>错误
//Fake::initializer();=>错误
void*fakeVar=false::initializer();//=>好啊
void main()
{
STD:C++中的CUT< P>,你不能在任何函数外都有语句。但是,你有全局对象声明,并且构造函数(初始化器)调用这些全局对象在主启动之前是自动的。在你的例子中,FAKEVAR是一个全局指针,它通过类静态范围的函数初始化,这绝对是好的。
即使是全局对象也会提供全局对象构造函数执行所需的初始化。
比如说,
class Fake
{
public:
Fake() {
myMap["test"]=222;
// Do whatever with your global Variables
}
};
Fake fake;
解决这个问题的一种方法是让一个类有一个构造函数来做事情,然后声明该类的一个伪变量
struct Initializer
{
Initializer()
{
// Do pre-main initialization here
}
};
Initializer initializer;
当然,您可以让多个此类类执行杂项初始化。每个翻译单元中的顺序指定为自上而下,但没有指定翻译单元之间的顺序。§8.5.2规定
除了使用constexpr说明符声明的对象之外
参见7.1.5,变量定义中的初始值设定项可以由
包含文本和先前声明的任意表达式的
变量和函数,与变量的存储持续时间无关
因此,你所做的是完全被C++标准所允许的,也就是说,如果你需要执行“初始化操作”,最好使用类构造函数(例如包装器)。
这是个好主意吗
不太可能。如果有人在他们“棘手的初始化”中决定了这一点怎么办他们想使用您的映射,但在某些系统或其他系统上,或者在特定的重新链接后由于不明显的原因,您的映射在尝试使用后被初始化?如果您让他们调用一个返回映射引用的静态函数,那么它可以在第一次调用时对其进行初始化。使映射成为其中的静态局部变量函数,您可以在没有此保护的情况下停止任何意外使用。您不需要假类……您可以使用lambda进行初始化
auto myMap = []{
std::map<int, string> m;
m["test"] = 222;
return m;
}();
automymap=[]{
std::map m;
m[“测试”]=222;
返回m;
}();
或者,如果只是普通数据,则初始化映射:
std::map<std::string, int> myMap { { "test", 222 } };
std::map myMap{{“test”,222};
使用下面的复杂代码是一个好主意吗(例如)
做一些std::map操作
没有
任何一种解决可变非局部变量的解决方案都是一个可怕的想法。
< P>你所做的是完全合法的C++。所以,如果它对你有用,并且是可以被其他人使用代码理解和理解的,那就没问题。Joachim Pileborg的例子对我来说更清楚。< /P>
初始化全局变量时,如果它们在初始化期间相互使用,则可能会出现这样的问题。在这种情况下,确保变量以正确的顺序初始化可能会很棘手。因此,我更喜欢创建InitializeX、InitializeY等函数,并从主函数显式地以正确的顺序调用它们行动
错误的顺序也会导致程序退出时出现问题,全局变量在其中一些可能已被销毁的情况下仍试图相互使用。同样,在主返回之前以正确顺序进行一些显式销毁调用可以使问题更清楚
<>所以,如果它对你有用,那就去注意它。同样的建议也适用于C++中的每个特性!
你在问题中说你自己认为代码是“棘手的”。没有必要为了它而使事情过于复杂。因此,如果你有一个对你来说不那么“棘手”的替代方案……那可能会更好。当我听到“棘手的代码”时我立刻想到代码气味和维护噩梦。回答你的问题,不,这不是一个好主意。虽然它是有效的C++代码,但却是一个很差的实践。还有其他更明确和有意义的替代方案来解决这个问题。方法返回void*NULL对于程序的意图来说是毫无意义的(即,代码的每一行都应该有意义的目的),现在您还有另一个不必要的全局变量fakeVar,它不必要地指向NULL
让我们考虑一些“棘手”的替代方案:
如果您只有一个myMap的全局实例这一点非常重要,那么使用单例模式可能更合适,并且您可以在需要时懒洋洋地初始化myMap的内容。请记住,单例模式本身也有问题
使用静态方法创建并返回映射或使用全局名称空间。例如,以下内容:
// 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;
}
//global.h
命名空间全局
{
外部std::map myMap;
};
//global.cpp
命名空间全局
{
std::map initMap()
{
地图;
map[“测试”]=222;
返回图;
}
std::map myMap=initMap();
};
//main.cpp
#包括“global.h”
int main()
{
std::cout在这种情况下,unity构建(单个翻译单元构建)可能非常强大。\uuuu COUNTER\uuuuu
宏是事实上的标准
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;
}
// 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>();
}
// 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
#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;
});