我长期使用Java并且对C ++来说相对较新。所以在Java中,如果在类级别存在一些复杂的静态对象(在Java中,一切都在类级别),我们可以简单地使用静态块来初始化它。 E.g。
js
然而,鉴于初始化C ++静态变量的限制,如果我想定义相同的public class MyClass extends MyBase {
public static final Map<String, AComplexClass> STATIC_MAP = new HashMap<String, AComplexClass> ();
static {
AComplexClass first = ComplexClassFactory.createComplexType1();
first.configure("Something", "Something");
STATIC_MAP.put("key1", first);
// do some more
}
}
,这可能过于复杂,这似乎我需要在每个{STATIC_MAP
文件中都有命名变量1}}对象然后放到地图上,在代码中创建了一些垃圾,并且其他类也偶然会引用它们。
那么这种静态初始化逻辑的C ++等效的最佳实践是什么?我认为这是使用相同的策略,但也许是其他一些风格。
答案 0 :(得分:4)
您可以在C ++中执行类似的操作,但需要在.cpp文件中进行初始化。您可以将初始化推迟到函数,以便您可以执行任何所需的初始化:
<强> MyClass.h 强>
#pragma once
#include <unordered_map>
#include <string>
#include "AComplexClass.h"
using MyMap = std::unordered_map<std::string, AComplexClass>;
class MyClass {
public:
const static MyMap STATIC_MAP;
};
<强> MyClass.cpp 强>
#include "MyClass.h"
namespace {
MyMap createMap() {
MyMap map;
auto first = createComplexType1();
first.configure("Something", "Something");
map.emplace("key1", std::move(first));
// do some more
return map;
}
}
const MyMap MyClass::STATIC_MAP = createMap();
<强>的main.cpp 强>
#include "MyClass.h"
int main() {
auto object = MyClass::STATIC_MAP.at("key1");
}
由于operator[]
需要非常量访问权限,我使用at()
代替。
你需要注意的一件事是static initialization order fiasco,这基本上意味着你应该避免依赖另一个.cpp文件中的任何静态初始化。
避免静态初始化顺序失败的一种方法是使映射成为函数中的静态局部变量。这样做的好处是,如果您愿意,可以在.h文件中定义所有内容:
<强> MyClass.h 强>
#pragma once
#include <unordered_map>
#include <string>
#include "AComplexClass.h"
using MyMap = std::unordered_map<std::string, AComplexClass>;
MyMap createMap() {
// as before ...
}
class MyClass {
public:
static const AComplexClass& STATIC_MAP(const std::string& key) {
const static MyMap map = createMap();
// Could return the map by reference but may as well use it in this
// function to make the syntax slightly simpler for the calling code.
return map.at(key);
}
};
<强>的main.cpp 强>
#include "MyClass.h"
int main() {
auto object = MyClass::STATIC_MAP("key1");
}
静态局部变量在首次使用时将被初始化。
答案 1 :(得分:3)
这是一个组织问题。我不确定是否有一个单一的最佳实践&#34;每个人都会同意,但这就是我接近你想要做的事情。
首先,行
public static final Map<String, AComplexClass> STATIC_MAP = new HashMap<String, AComplexClass> ();
您可以在C ++中执行此操作,但需要将其分解。假设您的课程以标准.h(pp)
/ .cpp
分隔实施,您需要:
.h(pp): static Map<String, AComplexClass> STATIC_MAP;
.cpp: Map<String, AComplexClass> MyClass::STATIC_MAP = new HashMap<String, AComplexClass> ();
这将创建您要用于静态对象的内存。但是,你只能使用那种语法真正做到这一点 - 没有设置(如果构造函数需要参数,我不知道如何使它工作 - 除非它们是常量,我认为你需要一个不同的方法)。对于设置,您有两种选择。
选项1:将其粘贴在构造函数
中如果您向该类添加另一个静态变量,例如static bool firstObject
/ bool MyClass::firstObject = true;
,那么您可以添加一些效果:
MyClass::MyClass()
{
if( firstObject )
{
// Do your setup
firstObject = false;
}
}
这会带来一些开销,但除非您正在创建疯狂数量的这些对象,否则它会非常小。
选项2:设置变量的工厂/朋友类
如果您有另一个明确负责创建课程的课程(例如工厂模式),您可以将该课程设为朋友并让它设置值。然而,这假设每个级别只有一个这样的工厂 - 尽管通常情况如此。
如你所知,你不需要使用这个选项 - 但是我说一般来说,你可能不想要非const
静态公开。这是一个难以追踪的混乱的秘诀,因为任何东西都可以在程序的任何地方修改它们。
答案 2 :(得分:2)
user4581301已经有了正确答案但可以改进。
在C ++中,您还可以静态初始化成员,让我们将您的Java类转换为C ++类:
class MyClass : public MyBase {
public:
static std::map<std::string, AComplexClass> STATIC_MAP;
};
这就是全部,不多也不少。正如您所看到的,STATIC_MAP
是该类的一部分,但未在此初始化,因此...应该初始化它?课外(似乎怪异 but this is the C++ way):
std::map<std::string, AComplexClass> MyClass::STATIC_MAP
{
{"key1", ComplexClassFactory::createComplexType1("Something", "Something")},
{"key2", ComplexClassFactory::createComplexType1("Foo", "Bar")},
{"key3", ComplexClassFactory::createComplexType1("Epi", "Blas")},
{"key4", ComplexClassFactory::createComplexType1("Mortadelo", "Filemon")},
{"key5", ComplexClassFactory::createComplexType1("Asterix", "Obelix")},
};
将其视为Java类上的static
块,但在类本身之外写入。上面的代码显示了C ++的一个新功能,因为C ++ 11标准可以使用initializer list来填充容器,所以对于类的地图,我们传递了{key, value}
对的列表到std::map
构造函数。关键是文字字符串"key?"
,值是createComplexType1
返回的值。
将静态初始化与类的定义分开允许我们在代码文件(cpp)而不是头文件(h)中执行此初始化,这样做有一些优点,比如更简洁的代码和隐藏细节的可能性该课程。
STATIC_MAP
的初始化和生命周期绑定到静态storage duration “:
静态 存储时间。 对象的存储空间在程序开始时分配,在程序结束时取消分配。只存在一个对象实例。在命名空间范围(包括全局命名空间)声明的所有对象都具有此存储持续时间,以及使用static或extern声明的持续时间
您可以看到现场演示 Here 。
答案 3 :(得分:1)
我错过了一件:让STATIC_MAP保持不变。你不能在const地图上使用operator [],因为如果查找的密钥不存在,地图就会成功。谨防。 C ++和Java地图之间的巨大差异。
#include<iostream>
#include <map>
#include <memory>
//Need a class for the example
class AComplexClass
{
public:
virtual ~AComplexClass()
{
}
// just to give it something to do. It is a complex class after all.
virtual std::string toString() = 0;
};
// Doing this with a map<string, AComplexClass> is trivial.
// To make things a bit nastier, let's throw in some abstraction!
class ChildOfAComplexClass: public AComplexClass
{
public:
std::string toString()
{
return " Howdy! I'm a child of AComplexClass!";
}
};
// shared_ptr will take care of the garbage collection since we're passing
// around a hierarchy of complex classes. Normally you wouldn't bother with
// a pointer and let map handle all of the allocation/deallocation, but I
// wanted to make this example hard.
// OK. Non-trivial.
// OK. It's still trivial.
// How much blood do you want from me?
std::shared_ptr<AComplexClass> AComplexClassFactory()
{
return std::shared_ptr<AComplexClass>(new ChildOfAComplexClass());
}
// The main event! Declare and load up the map.
std::map<std::string, std::shared_ptr<AComplexClass>> STATIC_MAP
{
{"one", AComplexClassFactory()},
{"two", AComplexClassFactory()},
{"three", AComplexClassFactory()},
{"four", AComplexClassFactory()}
};
// And a quick test to prove it works.
int main()
{
std::cout << STATIC_MAP["one"]->toString() << std::endl;
return 0;
}
Chris Drew指出方法中的std :: map&#39;解决了const问题,并让用户知道地图中是否存在密钥。请注意,处理异常可能非常昂贵,因此我不建议将它们用于流量控制。如果搜索不在地图中的键是常见用例,请使用map的find方法。
// The main event! Declare and load up the map.
const std::map<std::string, std::shared_ptr<AComplexClass>> STATIC_MAP
{
{"one", AComplexClassFactory()},
{"two", AComplexClassFactory()},
{"three", AComplexClassFactory()},
{"four", AComplexClassFactory()}
};
// And a quick test to prove it works.
int main()
{
try
{
std::cout << STATIC_MAP.at("one")->toString() << std::endl;
std::cout << STATIC_MAP.at("five")->toString() << std::endl;
}
catch (std::out_of_range &oore)
{
std::cout << "And five does not exist in the map!" << std::endl;
}
return 0;
}
答案 4 :(得分:1)
@ ChrisDrew关于使用额外函数进行初始化的回答:如果仅为此创建函数并且它非常小,那么使用lambda函数可能是个好主意。例如,当我想初始化静态向量时,我在.cpp中有这行代码:
//.cpp
vector<int> MyClass::v = [](){vector<int> v; v.reserve(100); return v;}();