【问题标题】:Store a list of all class instances as a static array within the class将所有类实例的列表存储为类中的静态数组
【发布时间】:2015-01-20 21:50:56
【问题描述】:

我确定以前有人问过这个问题,但是这个问题的术语令人困惑,而且我是新的顶级 C++ 我什至不确定我是否正确使用它,所以我无法发现它以前问过。如果我错过了,请原谅我。

(如果有人足够专家来纠正我在问题中的语法,请这样做,这样我将来可能会更准确;-))

我需要定义一个类,其中成员数组存储指向该类的所有实例的指针。该数组由每个成员都有的整数 id 索引。

因此访问:

MyClass testcase = MyClass();
int i = testcase.id;
MyClass* tempVar;
tempVar = MyClass::InstanceRecord[i]; // this is the procedure in ?

应该用指向新创建的类'testcase'实例的指针填充tempVar。这样,类成员可以通过他们的 'id' 成员相互引用。

我的问题在于声明数组“InstanceRecord”。我无法控制填充数组的顺序(换句话说,创建类的实例),并且 id 编号是预定义的[段落编辑]。因此我需要使用以下形式:(注意:这是在类定义中)

static MyClass* InstanceRecord[MAXIMUM_POSSIBLE_INSTANCES] = {x};

为了声明数组,并一步填充数组的整个宽度。这样,我可以在调用 MyClass()(构造函数)期间更改数组中给定元素的值。

问题是我不知道如何用尚不存在的内存指针填充数组。有人可以向我解释上一个语句中我的“=”右边的内容吗,或者提供另一种方法来做到这一点?

编辑:类定义如下所示:

class MyClass{
  Public:
    MyClass(string fileName){

      // Initialize class members from file data
      // including the id member

      // list of other members which require a status of True
      // before this item has an available of True.  id1-id5
      // represent id members of other instances of this class.
      // these id's all come from hard files.
      required_data[] = {id1, id2, id3, id4,id5};
      available=0; //boolean false
      status=0; //boolean false

      // is this the right way to do a vector of pointers?
      InstanceRecord[id]= this;
    }

    MyClass* get_instance_by_id(int i){
      return InstanceRecord[i];
    }

    bool is_available(){
      return available;
    }

    void update_available(){
      int sum = 0;
      for (int i = 0; i<5 ;i++){
        if (InstanceRecord[required_data[i]].status == 1 || required_data[i] == 0){
          sum +=1;
        }
      }
      if (sum >=5){
        available = 1;
      }
    }

  Private:
    int id;
    std::vector<MyClass*>InstanceRecord (MAX_INSTANCES, nullptr);
    int required_data[5];
    bool status;
    bool available;
}

编辑:示例数据文件(通常不允许添加注释,但为清楚起见添加了注释)

Widget Glue Removal Tool  // becomes MyClass.name
1251                      // becomes MyClass.id
...lots of other stuff... // becomes MyClass.[some_var_name]
14,37,1841,15,27          // becomes the id1...id5 which must be
                          // status checked to determine this
                          // object's available flag.  These ints
                          // are the id's (like this 1251) of those
                          // other instances.

这个数据实际上是针对我的一个宠物项目,它是一个视频游戏。该类代表“研究树”中的一个项目,其中必须在新主题可用之前完成先决条件研究。树和主题不一定是线性的。您可能必须先研究项目 20,400,12 和 15,然后才能访问项目 47(因此 id 为 20、400、12 和 15 的实例都必须具有 status=1 显示它们在项目 47 可用之前已完成= 1 在研究项目选择窗口上显示)。由于研究树是通过读取目录中的每个文件来填充的,因此以非受控顺序填充,我需要在创建类的第一个实例时用某种空指针(假设为 nullptr)填充我的索引向量.我最初的问题是并且基本上仍然是如何用这些数据填充向量。我现在知道了

std::vector<MyClass*>InstanceRecord(MAX_POSSIBLE_INSTANCES, X);

应该满足我的需要,但是 nullptr 可以替换此行中的 X 吗?

巴里答案中的工厂课程实际上教会了我很多,但下一个答案似乎更适合我的需要。在这一点上,我只是想确定迈克尔的答案是否可以简单地修改为:

id=<data read in from file>;
InstanceRecord[id]=this;

在他的答案的第 5 行和第 6 行。

正如我所说,我只是 C++ 的涉猎者,显然我对术语缺乏正确的理解,因此无法很好地说明问题,这导致了一些混乱。编译时间来测试这将是巨大的,所以我希望在我开始这个过程之前尽可能地让它几乎没有错误(不是我们所有人)。

【问题讨论】:

  • 原始指针在现代 C++ 中被认为是不好的做法,您可能需要查看 shared_ptr。鉴于 InstanceRecord 的可扩展性,使用vector 可能是有意义的。
  • 如果您只是想跟踪班级的每个实例,我相信下面@Barry 的答案会奏效。
  • 可能是这样,但我对 c++ 还很陌生,所以我必须逐行查看这个答案才能理解它...... ;-)
  • 矢量正在编辑中。这绝对是要走的路。

标签: c++ class pointers instance


【解决方案1】:

我认为你想要的是一个对象工厂:

template <typename T>
class Factory {
private:
    static std::vector<std::unique_ptr<T>> instances;

public:
    static T* create() {
        std::unique_ptr<T> obj{new T};
        obj->id = instances.size();
        instances.emplace_back(std::move(obj));
        return instances.back().get();
    }

    static T* get(size_t idx) const {
        return instances[idx].get(); 
    }
};

用法如下:

MyClass* testcase = Factory<MyClass>::create();
assert(testcase->id == 0);
assert(Factory<MyClass>::get(0) == testcase);

【讨论】:

  • 所以如果我理解这一点......我仍然定义 MyClass (如上但没有“跟踪元素”)并通过类 Factory 声明 MyClass 的新实例(用于创建MyClass OUTSIDE of class Factory),然后使用 Factory::get(x); 查找 MyClass 的实例 X;哪个返回指针?
  • @Jase 差不多。 MyClass 仍然需要 id
  • 这个工厂类看起来很有趣,但如果我没看错的话,它(工厂)正在根据创建顺序分配一个 id。请查看我编辑过的问题(我又刷新了课堂示例)。 update_available 函数是我需要这些以某种方式工作的全部原因。有没有办法使用工厂类型类来做到这一点?
  • 我知道这是一个老话题......但我有错误 undefined reference to Factory::instances
【解决方案2】:

您可以通过将跟踪向量添加为静态实例来实现此目的。 您还需要在销毁时从跟踪向量中删除该项目。如果您从多个线程创建/更新,则需要适当地锁定/解锁它。

不加锁可能有点像这样。

class MyClass{
  Public:
    MyClass(string fileName){  
      status=0;
      id = instanceRecord.size();
      instanceRecord.push_back(this);          
    }

   ~MyClass() {
      //Clear it so that we dont access invalid data in update_available.
      instanceRecord[id]=NULL;
   }

    static MyClass* get_instance_by_id(int i){
      //TODO: Add bounds checking here.
      return instanceRecord.get(i);
    }    

    void update_available(){
      int sum = 0;
      for (int i = 0; i<5 ;i++){
        MyClass * instance = instanceRecord[i];
        if(instance==NULL)
          continue;  
        if (instanceRecord[required_data[i]].status == 1 || required_data[i] == 0){
          sum +=1;
        }
      }
      if (sum >=5){
        available = 1;
      }
    }

  private:
    int id;
    static std::vector<MyClass*>instanceRecord;
    ...
}

然后您需要添加一个 .cpp 文件以添加静态向量 - 类似这样。

std::vector<MyClass> MyClass::instanceRecord = std::vector<MyClass>();

如果您需要锁定,那么您需要添加一个静态锁定对象,并使用该锁定包装对 instanceRecord 的所有访问。

最后一个附带条件是,如果您在 main 之外创建 MyClass 的任何实例,由于使用静态变量,您可能会遇到 "static initialization order fiasco"。使用某些单例方法可以解决这个问题,但它们会使事情看起来更加复杂,因此在简单的情况下不值得。

【讨论】:

  • 这看起来更像我所追求的,我唯一的问题是'id'分配。我编辑了问题以添加示例数据文件(类似于将用于实例化类对象的文件),并使用 cmets 来解释字段用法。您能否回顾一下并告诉我这将如何修改您的建议?
  • 如果您不使用id 作为顺序计数器,而是需要将其作为输入,则需要从std::vector&lt;MyClass*&gt; 中的跟踪切换到std::map&lt;int,MyClass*&gt;。您可能还想检查 id 的唯一性 - 您也可以使用地图。
猜你喜欢
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-29
  • 2017-04-04
  • 1970-01-01
相关资源
最近更新 更多