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

时间:2013-11-25 20:27:24

标签: c++

我需要跟踪特定类创建的所有对象,我需要使用标识符字符串来访问它们。以下代码几乎完全符合我的需要。 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() - function)的实现是在基类BaseNamedObject中完成的。存储所有对象的结构的实现也位于NamedObjectStore类中,并隐藏在其方法后面。这允许我根据需要轻松更改这两个实现,而无需触及所有派生类。

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

所以这就是我的问题:有没有更好的方法来解决这个问题?或者我只需要在每个派生类中使用这四行代码?

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

托马斯

1 个答案:

答案 0 :(得分:2)

如我的评论中所述,您可以使用curiously recurring template pattern解决此问题。下面的代码使用您的原始示例,模板实际存储的类型:

#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;
}