【问题标题】:getting all objects of a classes in C++在 C++ 中获取类的所有对象
【发布时间】:2013-07-04 18:28:36
【问题描述】:

有没有一种方法可以让我们在 C++ 中获取一个类的所有对象。就像在 Python 中我们可以做的那样

class_name.objects.all() 

获取一个类的所有对象。如果存在,它在 C++ 中的类比是什么?

【问题讨论】:

  • 如果你问的是Objective-C,我会说“也许”。在 C++ 中,我很确定你不能,至少不能直接。
  • 抱歉,在 C++ 中,Django 已被链接。
  • 但是你可以创建一个类,构造函数将this作为smart_ptr放入静态vector,而析构函数将其删除。
  • 你想解决什么问题,这是你的解决方案?
  • @H2CO3:你不必要地把事情复杂化了。在这种情况下,原始指针可以正常工作。不需要智能指针。

标签: c++ class object


【解决方案1】:

您可以自己执行此操作,但请确保您知道自己在做什么。

如何:

C++ 中没有任何东西已经做到了这一点,但你自己很容易做到这一点。关键是要认识到一个类可以具有静态成员变量和函数(即属于整个类的函数,而不是属于该类的单个对象)。

因此您可以使用某种表或其他数据结构来存储对每个对象的引用。像这样:

class A {

public:
  //constructor assigns this object an id based on the static value curID,
  //which is common to the class (i.e. the next class to call the constructor 
  //will be assigned an id thats 1 more than this one
  //also, constructor adds the pointer to this object to a static map of ids 
  //to objects. This way, the map can be queried for the pointer to an object 
  //that has a particular id
  A() { 
    id = curID++;
    objects[id] = this;
  }

  //copy constructor ensures an object copied from another does not 
  //take the id of the other object, and gets added to the map
  A(const A&) {
    id = curID++; //don't want have the same ID as the object we are copying from
    objects[id] = this;
    x = A.x;
    y = A.y;
  }

  A&  operator=(const A&) {
    id = curID++;
    objects[id] = this;
    x = A.x;
    y = A.y;
    return *this; 
  }

  //destructor removes the pointer to this object from the map
  ~A() {
    objects.erase(id);
  }

  //function to get the map that stores all the objects
  static map<int, A*>& GetMapOfObjects() { 
    return objects;
  }

private:
  //the following variable is **static**, which means it does not
  //belong to a single object but to the whole class. Here, it is
  //used to generate a unique ID for every new object that's 
  //instantiated. If you have a lot of objects (e.g. more than
  //32,767), consider using a long int
  static int curID;  

  //this variable is also static, and is map that stores a pointer
  //to each object. This way, you can access the pointer to a
  //particular object using its ID. Depending on what you need, you
  //could use other structures than a map
  static map<int, A*> objects; 


  //this is a (non-static) member variable, i.e. unique to each object.
  //Its value is determined in the constructor, making use of curID.
  int id;

  //these are some other member variables, depending on what your object actually is
  double x;
  double y; 
}

注意:上述设计非常基本且不完整,只是为了让您了解如何使用静态成员/函数来实现您所要求的功能。例如,对于您想要对所有对象执行的操作,例如,最好实现一个遍历元素映射的静态函数,而不是获取映射然后在“外部”进行迭代。

为什么:

我自己从未使用过这种方法,但我能想到的一个潜在用例是,例如在图形或游戏应用程序中,您可能只想绘制范围内的对象并一次性更改所有对象的某些绘图相关属性,例如颜色或大小。我正在开发一个最终可能需要这样的应用程序(一种可视化调试器)。我相信人们可以在 cmets 中提供更多示例。

为什么不:

当涉及到继承时,情况会变得复杂。

  • 如果您有一个派生自 A 的 B 类(即 B“是”A),那么谁应该跟踪 B 的对象? A 中的对象的静态成员,或 B 中的类似对象,或两者兼而有之?
  • 让我们说两个。那么如果一个适用于 A 中所有对象的静态函数在每个对象上调用一个虚拟成员函数会发生什么?如果虚函数已在派生类中被覆盖,则将为 A 类中跟踪的所有实际上是 B 对象的对象调用该函数。如果你在 B 中的另一个静态函数中再次调用该函数会发生什么?

【讨论】:

  • 你忘记了赋值运算符。
  • @MartinJames 同意。互斥锁可用于控制对映射的访问,但也有通常的缺点(开销、实现和接口的复杂性等)
  • @MartinJames:您可以将容器操作放入单独的子例程中,并使用任何可用的机制来保护它。或者您可以维护多个映射 - 每个线程 id 一个映射。
【解决方案2】:

如前所述,C++ 不提供自动执行此操作的机制。但是(再次在 cmets 中已经说明)您可以使用其中一个标准库容器来维护已创建对象的列表,然后在构造函数中注册它们并在析构函数中取消注册它们。下面的示例显示了执行此操作的一种方法...

#include <iostream>
#include <memory>
#include <utility>
#include <map>
#include <algorithm>
#include <iterator>
#include <typeinfo>
#include <vector>

class Object
{
    static std::map<const Object*, Object*> objects_;

public:

    Object()
    {
        objects_.insert(std::make_pair(this, this));
    }

    virtual ~Object()
    {
        objects_.erase(this);
    }

    static std::vector<Object*> get_all()
    {
        std::vector<Object*> o;
        o.reserve(objects_.size());
        for (auto obj : objects_)
        {
            o.push_back(obj.second);
        }
        return std::move(o);
    }

    template<class Type>
    static std::vector<Type*> get_bytype()
    {
        std::vector<Type*> o;
        for(auto obj : objects_)
        {
            Type *t = dynamic_cast<Type*>(obj.second);
            if (t != nullptr)
            {
                o.push_back(t);
            }
        };
        return std::move(o);
    }


    void print() const
    {
        std::cout << "I'm a " << typeid(*this).name() << " object @ " << this << std::endl;
    }
};

std::map<const Object*, Object*> Object::objects_;

class Foo : public Object {};
class Bar : public Object {};
int main()
{
    std::unique_ptr<Object> o1 = std::unique_ptr<Object>(new Foo());
    std::unique_ptr<Object> o2 = std::unique_ptr<Object>(new Bar());
    std::unique_ptr<Object> o3 = std::unique_ptr<Object>(new Foo());
    std::unique_ptr<Object> o4 = std::unique_ptr<Object>(new Bar());

    std::vector<Object*> objects = Object::get_all();
    for (auto o : objects)
    {
        o->print();
    }

    std::cout << "-----" << std::endl;

    std::vector<Foo*> foos = Object::get_bytype<Foo>();
    for (auto o : foos)
    {
        o->print();
    }

    std::cout << "-----" << std::endl;

    std::vector<Bar*> bars = Object::get_bytype<Bar>();
    for (auto o : bars)
    {
        o->print();
    }
}

上面的例子产生以下输出

我是 Foo 类对象 @ 003FED00
我是类 Bar 对象 @ 003FED30
我是 Foo 类对象 @ 003FED60
我是类 Bar 对象@003FED90

我是 Foo 类对象 @ 003FED00
我是 Foo 类对象 @ 003FED60

我是类 Bar 对象 @ 003FED30
我是类 Bar 对象@003FED90

【讨论】:

    【解决方案3】:

    据我所知没有办法,但您可以使用static 成员实现一种方法

    #include <iostream>
    #include <vector>
    class MyClass{
    private:
     static std::vector<MyClass*> objList;
    public:
     MyClass() {
      objList.push_back(this);
     }
     static std::vector<MyClass*> getAllObjects(){
      return objList;
     }
    };
    
    std::vector<MyClass*> MyClass::objList;
    
    main(){
     MyClass m,a;
     for (int i=0;i<MyClass::getAllObjects().size();i++){
      std::cout<<MyClass::getAllObjects()[i]<<std::endl;
     }
    }
    

    【讨论】:

      【解决方案4】:

      不,除非您自己实现此机制。默认情况下,它不是由 C++ 语言提供的。

      你可以很容易地自己实现这个机制——在构造函数中的某种表中注册类,在析构函数中取消注册。只要关注rule of Three就可以了。

      【讨论】:

      • 但无论如何,如果需要这样做,那么设计就完全不对了。
      • @H2CO3 同意。这对 Django 的状态有什么影响? :)
      • @H2CO3:不。如果您希望能够通过某种 ID 定位类的实例,则必须实现此机制。我能想到的一种场景是游戏引擎。
      • @SigTerm 我怀疑“必须”是否如此严格。同样,您可以创建一个将 ID 作为其参数的构造函数,等等。这确实表示严重的设计失败。
      • @H2CO3:“个性化”我没有个性化。只是你的文风让我想起了十年前的自己。 (观点)我“看到”了一些问题,如果你解决了它们,你就会成为更好的程序员,所以我想我会指出它们。问题: 1.“错误”没有解释。如果你说“错”,你应该能够解释“为什么”。 2、如果你不能解释“为什么”,那就是没有根据的信念。 3. 专制主义。 “这个技巧是对的,这个不是”,可能意味着你受到了某人的影响,但还没有真正考虑过。 4. 批评被视为人身攻击。
      【解决方案5】:

      当然有。只需使用 Factory 模式创建和销毁所有对象,并在 Factory 实现中,在您将提供的 Factory 函数中返回活动对象的集合。

      【讨论】:

      • 我没有投反对票,但 IMO 我发现这被过度设计了。一个包含对象指针/引用的静态容器就足够了,而且比这简单得多。此外,要求工厂创建所有对象的问题在于它与标准容器(AFAIK)的融合效果不佳。如果这是一个 Java 问题,我觉得这个解决方案会更好。
      • 我不敢相信修改每个对象的构造函数和析构函数或引入静态元素被认为是比实现工厂模式更好的解决方案。
      • @Borgleader 过度设计?看看上面的解决方案。我们不知道这是 OP 可能对应用程序将创建的一组对象的唯一要求。将所有内容集中在工厂中是恕我直言更好的方法。没有什么可以阻止 Factory 将一些 STL 容器作为内部实现。
      • 需要我提醒我不是反对你的人吗?我给了你我的意见。否决您的人可能有完全不同(可能更好)的理由。至于我对标准容器的评论,我不是在谈论工厂的内部结构。
      • @Tony:我的推理是基于 KISS 原则。静态容器将占用 3..5 行代码(假设类已经有构造函数/析构函数)。工厂将需要额外的类,而且您可能必须将其设为单例,另外您必须添加额外的特殊对象创建机制并确保您在任何地方都使用它,此外您还必须创建类(由工厂构建)非默认构造和不可复制,否则临时变量不会注册等。因此,我认为没有理由使用工厂。隐藏容器在 3..5 行代码中涵盖了所有内容。
      猜你喜欢
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 2015-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多