简体   繁体   English

在 C++ 中跟踪类实例化的概念

[英]Concept to keep track of class instantiations in C++

I am trying to write a code that keeps track of instances of my class.我正在尝试编写一个代码来跟踪我的类的实例。 Each instance is uniquely identified by a type (int).每个实例都由type (int) 唯一标识。 I would like to have some kind a map which links a type to a instantiation of my class.我想要某种映射,将type链接到我的类的实例化。

My idea was to use a static map for this, and every instantiation registers itself when the constructor is called:我的想法是为此使用静态映射,并且在调用构造函数时每个实例化都会注册自己:

#include <unordered_map>
#include <iostream>

class Dummy {
    public:
    Dummy(double a, double b, double c, int type);
   
    void GetContents() {std::cout << type_ <<": a: " << a_ << " b: " << b_ << ", c: " << c_ << std::endl;}
    private:
        const double a_;
        const double b_;
        const double c_;
        const int type_;
};

static std::unordered_map<int, Dummy> Type_Map{{12, Dummy(1, 2, 3, 12)}}; // pre-defined instantiation

Dummy::Dummy(double a, double b, double c, int type) : a_(a), b_(b), c_(c), type_(type) {
        Type_Map.insert({type, *this});
    };

In this case, everything seems to work well:在这种情况下,一切似乎都运行良好:

int main()
{
    Dummy(12, 6, 23, 3);
    Dummy(8, 22, -4, 7);
    for (auto a : Type_Map) {
        std::cout << a.first << ", ";
        a.second.GetContents();
    }

}

which gives a correct output:这给出了正确的输出:

3, 3: a: 12 b: 6, c: 23
7, 7: a: 8 b: 22, c: -4
12, 12: a: 1 b: 2, c: 3

However, outside of this minimal example and in my real-life project, this seems to be unstable.然而,在这个最小的例子和我的现实项目之外,这似乎是不稳定的。 While the instantiation is still registered in the map inside of the constructor, the map entry is lost as soon as I leave the constructor.虽然实例化仍然在构造函数内部的映射中注册,但一旦我离开构造函数,映射条目就会丢失。

I believe this may have to do with the *this call inside the constructor where the object is not yet correctly initialized?我相信这可能与对象尚未正确初始化的构造函数中的*this调用有关? Is this ill-defined behavior?这是定义不明确的行为吗?

Is there a better concept that achieves what I want to do?有没有更好的概念来实现我想做的事情?

PS: What I actually need PS:我真正需要的

The code is written this way because I have some instantiations of my StaticData class and many instantiations of my DynamicData class.代码是这样编写的,因为我有一些StaticData类的实例化和DynamicData类的许多实例化。 The content of StaticData instantiations do not change (all members are const ), and there are only a few instantiations (this corresponds to the Dummy class in my minimal example). StaticData实例化的内容不会改变(所有成员都是const ),并且只有几个实例化(这对应于我的最小示例中的Dummy类)。

However, there are a lot of DynamicData instantiations.但是,有很多DynamicData实例化。 For every DynamicData instantiation, there is a specific StaticData object which belongs to it.对于每个DynamicData实例化,都有一个属于它的特定StaticData对象。 I want to avoid that every DynamicData object has to carry a StaticData object.我想避免每个DynamicData对象都必须携带一个StaticData对象。 Therefore, it only carries an id that links it uniquely to a StaticData object.因此,它只携带一个 id 将它唯一地链接到一个StaticData对象。 If the user wants to find out which StaticData belongs to my specific DynamicData object, we can just look in the map (this will not happen regularly during runtime, but we still need to keep the information).如果用户想找出哪个StaticData属于我的特定DynamicData对象,我们可以在地图中查看(这在运行时不会定期发生,但我们仍然需要保留该信息)。

The obvious solution would be to just use pointers.显而易见的解决方案是只使用指针。 However, I want the user to be able to just initialize a DynamicData object with the id instead of having to pass a pointer to a StaticData object.但是,我希望用户能够仅使用id初始化DynamicData对象,而不必传递指向StaticData对象的指针。

What you want is a registry, a specific object where you create all of the StaticData objects you will ever intend to use (along with their associated IDs).您想要的是一个注册表,一个特定的对象,您可以在其中创建您打算使用的所有StaticData对象(以及它们关联的 ID)。 If someone needs a particular StaticData configuration, they add it to the registry.如果有人需要特定的StaticData配置,他们会将其添加到注册表中。 Other code can reference objects in the registry by ID.其他代码可以通过 ID 引用注册表中的对象。

The important point is that they do not reference them by constructing an object.重要的一点是它们不会通过构造对象来引用它们。 They never make a StaticData ;他们从不制作StaticData they ask the registry for a pointer/reference to one which already exists.他们要求注册表提供一个指向已经存在的指针/引用。 The lifetime of all such objects is controlled and managed in the registry, and nobody else gets to manage such things.所有这些对象的生命周期都在注册表中控制和管理,没有其他人可以管理这些事情。

You're trying to make the registry implicit by hiding it behind StaticData 's constructor.您试图通过将注册表隐藏在StaticData的构造函数后面来使注册表隐式。 That is fertile ground for creating lifetime issues, as the ownership relationship between the StaticData you create and the one stored in the registry is... murky.这是创建生命周期问题的沃土,因为您创建的StaticData与存储在注册表中的StaticData之间的所有权关系是……模糊的。 It's best to make it explicit by directly asking the registry for a reference to the object.最好通过直接向注册表询问对象的引用来使其明确。 It makes it clear that you get to access the object, but you don't own it.它清楚地表明您可以访问该对象,但您并不拥有它。

Your problem is that the static map is filled inside the constructor, but outside of the constructor, the entry is gone.您的问题是静态映射在构造函数内部填充,但在构造函数外部,条目消失了。 It seems like there are two different instances of the static map.似乎静态地图有两个不同的实例。 You see one instance inside the constructor, and another instance outside of the constructor.您会在构造函数内部看到一个实例,在构造函数外部看到另一个实例。

It looks like, you define your static map in a header file.看起来,您在头文件中定义了静态地图。 Each .cpp file that includes the header file will "see" its own instance of the static map.每个包含头文件的 .cpp 文件将“看到”它自己的静态映射实例。 Solution: Make a class which keeps the map as a static member.解决方案:创建一个将地图保留为静态成员的类。 If this does not solve your problem, make also sure that you define the static member inside a .cpp file, like:如果这不能解决您的问题,请确保您在 .cpp 文件中定义静态成员,例如:

map<int, Dummy> TypeRegistry::Type_Map; map<int, Dummy> TypeRegistry::Type_Map;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM