c++:跟踪所有现有对象

c++: Keeping track of all existing objects

本文关键字:对象 跟踪 c++      更新时间:2023-10-16

我需要跟踪特定类创建的所有对象,并且需要使用标识符字符串访问它们。下面的代码几乎正是我所需要的。类NamedObject有一个静态成员m_object_by_name,它将名称(字符串)映射到对象,构造函数将每个创建的对象添加到映射中,析构函数从映射中删除删除的对象。

#include <map>
#include <string>
#include <iostream>
using namespace std;
class NamedObject
{
public:
     static NamedObject *object_by_name(const string &name) {
      return m_object_by_name[name];
     }
     NamedObject(const string &name) : m_name(name) {
      m_object_by_name[m_name] = this;
     }
     ~NamedObject() {
      m_object_by_name.erase(this->m_name);
     }
     const string &name() const{
      return m_name;
     }
private:
     string m_name;
     static map<string, NamedObject *> m_object_by_name;
};
map<string, NamedObject *> NamedObject::m_object_by_name;

int main ()
{
     new NamedObject("name1");
     new NamedObject("name2");
     NamedObject *obj1 = NamedObject::object_by_name("name1");
     NamedObject *obj2 = NamedObject::object_by_name("name2");
     cout << obj1->name() << endl;
     cout << obj2->name() << endl;
}

现在我有几个类,它们的对象需要通过名称来访问。从上面的NamedObject类继承当然存在这样的问题,即所有这些类都会共享它们的名称(例如,我不能有两个不同类但名称相同的对象),因为它们共享映射m_objects_by_name。此外,当使用object_by_name()方法访问对象时,我总是必须从NamedObject转换为实际的类。

我目前使用的这个问题的解决方案可以在下面的代码中看到。然而,我对这个解决方案并不满意(见下面的评论)。模板类NamedObjectStore现在负责存储类T的所有对象。此外,还有一个基类处理具有名称的属性和真正使用的派生类。派生类有一个静态NamedObjectStore对象,它在创建时添加对象,在删除时删除对象。

#include <map>
#include <string>
#include <iostream>
using namespace std;
template <class T>
class NamedObjectStore
{
public:
     void add_object(T *obj) {
      m_object_by_name[obj->name()] = obj;
     }
     void rem_object(T *obj) {
      m_object_by_name.erase(obj->name());
     }
     T *object_by_name(const string &name) {
      return m_object_by_name[name];
     }
private:
     map<string, T *> m_object_by_name;
};
class BaseNamedObject
{
public:
     BaseNamedObject(const string &name) : m_name(name) {
     }
     const string &name() const {
      return m_name;
     }
private:
     string m_name;
};
class DerivedNamedObject : public BaseNamedObject
{
public:
     static NamedObjectStore<DerivedNamedObject> store;
     DerivedNamedObject(const string &name) : BaseNamedObject(name) {
      store.add_object(this);
     }
     ~DerivedNamedObject() {
      store.rem_object(this);
     }
};
NamedObjectStore<DerivedNamedObject> DerivedNamedObject::store;

int main ()
{
     new DerivedNamedObject("name1");
     new DerivedNamedObject("name2");
     DerivedNamedObject *obj1 = DerivedNamedObject::store.object_by_name("name1");
     DerivedNamedObject *obj2 = DerivedNamedObject::store.object_by_name("name2");
     cout << obj1->name() << endl;
     cout << obj2->name() << endl;
}

从积极的方面来说,使对象成为命名对象(即name()-函数)的实现是在基类BaseNamedObject中完成的。此外,存储所有对象的结构的实现位于NamedObjectStore类中,并隐藏在其方法后面。这使我可以根据需要轻松地更改这两种实现,而无需接触所有派生类。

消极的一面是,我仍然需要一遍又一遍地打同样的东西。更准确地说,对于每个派生类(如DerivedNamedObject),我必须声明和定义静态成员存储,我必须在构造函数中向存储添加对象,并在析构函数中从存储中删除它们。

所以我的问题来了:有更好的方法来解决这个问题吗?还是我必须在每个派生类中使用这四行代码?

希望得到一些鼓舞人心的建议:-)

Thomas

正如我的评论中所说,您可以使用奇怪的重复模板模式来解决这个问题。下面的代码使用您的原始示例,以实际存储的类型为模板:

#include <map>
#include <string>
#include <iostream>
using namespace std;
template<class T>
class NamedObject
{
public:
     static NamedObject *object_by_name(const string &name) {
      return m_object_by_name[name];
     }
     NamedObject(const string &name) : m_name(name) {
      m_object_by_name[m_name] = this;
     }
     virtual ~NamedObject() {
      m_object_by_name.erase(this->m_name);
     }
     const string &name() const{
      return m_name;
     }
private:
     string m_name;
     static map<string, NamedObject *> m_object_by_name;
};
template <class T>
map<string, NamedObject<T> *> NamedObject<T>::m_object_by_name;
class A : public NamedObject<A>
{
public:
   A(const std::string& name) : NamedObject(name)
   {}
};
class B : public NamedObject<B>
{
public:
   B(const std::string& name) : NamedObject(name)
   {}
};
int main()
{
   new A("Test");
   new B("Test");
   auto one = A::object_by_name("Test");
   auto two = B::object_by_name("Test");
   cout << one << " - " << one->name() << "n";
   cout << two << " - " << two->name()  << "n";
   delete two;
   delete one;
}