【问题标题】:C++ Vectors of Objects and Pointers对象和指针的 C++ 向量
【发布时间】:2010-12-01 03:54:15
【问题描述】:

这是一个人为的例子,说明了我遇到的一个问题。基本上,我创建一个对象向量,然后创建一个指向对象的指针向量,然后打印指针和取消引用的对象。

#include <vector>
#include <iostream>

using namespace std;

namespace {
    struct MyClass {
        int* MyInt;
        MyClass(int* i) : MyInt(i) {}
    };

    struct MyBigClass {
        vector<MyClass> AllMyClassRecords;  // Where I keep the MyClass instances
        vector<int> TheInts;

        void loadMyClasses();
        void readMyClasses();
        MyBigClass() {}
    };

}

void MyBigClass::loadMyClasses() {
    for (int i = 0; i < 10; ++i) {
        TheInts.push_back(i);   // Create an int
        int *j = &TheInts[TheInts.size() - 1];  // Create a pointer to the new int
        AllMyClassRecords.push_back(MyClass(j));    // Create a MyClass using pointer
    }
}

void MyBigClass::readMyClasses() {
    for (vector<MyClass>::iterator it = AllMyClassRecords.begin();
            it != AllMyClassRecords.end(); ++it)
        cout << it->MyInt << " => " << *(it->MyInt) << endl;
}

int main() {
    MyBigClass MBC;

    MBC.loadMyClasses();
    MBC.readMyClasses();
}

基本上,我想创建一个指向另一个整数向量的指针向量。问题是这段代码打印出以下内容:

0x97ea008 => 159293472
0x97ea02c => 1
0x97ea040 => 2
0x97ea044 => 3
0x97ea078 => 4
0x97ea07c => 5
0x97ea080 => 6
0x97ea084 => 7
0x97ea0d8 => 8
0x97ea0dc => 9

除了第一个值外,它似乎按预期工作,这可能是内存中的一些垃圾。为什么只有第一个值受到影响?如果我的代码坏了,为什么只插入第一个指针就坏了?


更新:我在 Ubuntu 上使用 g++ 编译它。至于我正在做什么,我正在创建一个编译器分析通道。 MyClass 对象包含有关指令的信息,当我找到某些寄存器时,我想更新这些信息。寄存器编号索引向量向量,因此特定寄存器编号将具有MyClass*s 的向量。因此,如果找到寄存器,则向量中的任何MyClass 指针都将用于更新单独的MyClass 向量中保存的MyClass 对象。因为我正在累积存储在MyClass 对象中的指令信息和必须遵循MyClass 指针的寄存器信息,所以我不能先创建整个MyClass 向量而不创建单独的传递,我想避免.


更新2:现在有图片...

Pass Progress           inserts...   InstRecs (TheInt)  and updates...  UpdatePtrs (MyClass) 
----------------------              ------------------                  -----------------------
| => I1: Uses r0, r1 |              | InstRec for I1 |                  | r0: InstRec for I1* |
|    I2: Uses r0, r2 |              ------------------                  | r1: InstRec for I1* |
----------------------                                                  -----------------------

首先,pass 插入一个 InstRec,其中包含有关 I1 的信息。它还创建指向这个由寄存器编号索引的新 InstRec 的指针。这里的 r0 实际上是一个元素的向量,它指向 I1 的 InstRec,因此如果在后续指令中再次遇到 r0,则会更新 I1 的 InstRec。

Pass Progress           inserts...   InstRecs (TheInt)  and updates...  UpdatePtrs (MyClass) 
----------------------              ------------------                  -----------------------
|    I1: Uses r0, r1 |              | InstRec for I1 |                  | r0: InstRec for I1* |
| => I2: Uses r0, r2 |              | InstRec for I2 |                  |     InstRec for I2* |
----------------------              ------------------                  | r1: InstRec for I1* |
                                                                        | r2: InstRec for I2* |
                                                                        -----------------------

同样,第二个条目将被插入到 InstRecs 中,并且指针将被添加到 UpdatePtrs 结构中。由于 I2 使用 r0,另一个 InstRec 指针被推到 r0 向量。未显示的是:当检测到 I2 使用 r0 时,pass 在 UpdatePtrs 结构中查找 r0 指针向量,跟随每个指向其 InstRec 条目的指针,并使用新信息更新 InstRec。

希望这能让我想要做的事情更加清晰。我已经实现了@MerickOWA 首次提出的使用 InstRec 向量索引而不是 InstRec 指针的建议(因为一旦将 InstRec 添加到数组中,它们就永远不会移动),它现在似乎正在工作。

【问题讨论】:

  • 如果你在 Release 中编译和运行,我想你会发现它不仅仅是第一个坏的,而是所有的。
  • 我完全糊涂了。这无疑部分是由于我对您的问题领域不熟悉。你能用更笼统的说法重新表述一下,以便像我这样的傻瓜能理解吗?
  • 查看我的编辑。它可能在正确的道路上,也可能不在正确的道路上。
  • 你真的需要像访问 MyClass 这样的随机访问数组吗?如果您仅使用指针向量来访问 MyClass,那么向量的额外功能并不是真正有用,并且只会在向量移动所有内容时引起问题。也许试试 std::list 代替 MyClass?
  • @John:我用一些图表再次更新了这个问题。我在我的代码中解决了这个问题,但我在问题中添加了更多信息,以防你仍然感兴趣。

标签: c++ pointers vector


【解决方案1】:

你所做的与创建这样的东西非常相似:

vector<int> MyInts;
vector< vector<int>::iterator > MyIntIters;

然后每次向MyInts 添加新的int 时,都会获取迭代器并将该迭代器推入MyIntIters

您不能对 vector 执行此操作,因为在您向 MyInts 添加新 int 时,迭代器可能会失效。

所以你的整个结构都被破坏了。你需要想出一个全新的设计。首先我要问你为什么要一个指向另一个向量的迭代器(或指针)向量。是为了排序吗?以某种方式索引?还有什么?肯定有更好的方法来做你想做的任何事情。 你正在尝试做什么将有助于确定如何去做。

编辑:

在多次阅读和重读您的更新之后,在我看来,您似乎正在尝试创建一个 gloms 向量。也就是说,一个可变长度的缓冲区,开头有结构化数据,后面有其他内容。

这是一项相当棘手的业务,它需要动态分配。但是,您没有使用动态分配——您将对象按值推入向量中。指向这些对象的指针会随着向量大小的调整和洗牌而改变。

因此,如果这是您要执行的操作,则需要进行更改,以便使用new 创建您的glom,并将指向glom 的指针推到向量上。不过,这会打开一整个潘多拉魔盒。你做多大的缓冲区?你如何解析glom?你如何处理深拷贝、调整大小等?你如何正确地释放gloms而不像筛子一样泄漏?正如我所说,棘手的业务。在我的工作中,我们一直在做这种事情,并且有一堆我们使用的标准做法。这些需要大量的努力和大量的反复试验才能得到正确的结果,但我们仍然发现问题。如果这是你正在做的事情,你可能会考虑另一种方式。

【讨论】:

  • 我更新了我的问题,提供了有关特定应用程序的更多信息。
  • 第二个向量包含简单的结构体,其中包含指向整个第一个向量的指针和索引?换句话说,第二个向量不会是被解释为另一组数据的内存地址的数据集合,而是“知道”如何找到实际值的语义函数的集合。我在矩阵上做了类似的事情(虽然不是在 C++ 中),你可以检索单个单元格,这些单元格本质上不是数据对象,而是从实际矩阵中检索值的函数集合。
【解决方案2】:

它不仅在第一个被破坏,而且恰好是唯一一个被破坏的。如果您注意到,指针 0 到 7 指向不同的内存块,而它们应该彼此相邻。

std::vector 可以在任何时候为动态对象重新分配它需要的空间,你可以在向量中添加一些东西并且它没有足够的容量。在添加或插入可能增加其大小的内容后,您不能依赖指针(或迭代器)仍然有效。

有很多类似的解决方案

a) 您可以提前预留空间(使用 vector::reserve),这样就不会发生这种动态重新分配,但您必须知道要添加的最大对象数。

b) 等到所有对象都添加完毕后,再获取指向对象的指针

c) 使用对象的索引和指向向量的指针作为一对来引用您的对象,而不是直接引用对象指针。它的工作量更大,但不会改变(假设您没有在开头或中间插入/删除对象)

d) 尝试检测向量何时重新分配其数据(vector::capacity 返回不同的值)并刷新指针向量并重建它

e) 对不会在 std::list 等更改上重新分配的对象使用不同的容器,并放弃对基础对象容器的随机访问,您仍然可以使用指针向量,但现在指针不会变成无效。 (你真的需要随机访问和指向对象的指针吗?)

f) 重新考虑您的设计,使其不需要此类指针

【讨论】:

    【解决方案3】:

    您正在尝试存储指向向量内容的指针。这不起作用,因为向量被设计为在内存中移动其内容以实现调整大小。

    根据您的需要,您可以:

    • 将索引存储到向量中

    • 发明一些其他类型的“把手”

    • 更改向量以存储指向实例的智能指针(将使用“new”分配并单独存在),然后将另一个智能指针存储到同一实例。智能指针将维护一个引用计数,并在实例不再被向量或外部代码引用时释放内存。 (当然,为此存在预制的解决方案:例如,请参阅 boost::shared_ptr。)

    【讨论】:

      【解决方案4】:

      push_back 使指向向量的迭代器和指针无效。发生了什么:

      1. 你推送到 TheInts
      2. 然后您将指针指向刚刚推送的元素并将其存储在另一个向量中

      上面第 1 点的推入可能会重新分配一个向量,并使之前指向前面元素的所有指针无效。他们指向的值超出了您的控制范围。

      为什么它只发生一次 - 只是运气(向量分配的块可能比当前需要的更大)。

      使用 dequeue 或 list 或预先保留所需的空间(如果您可以确定需要多少)或在完成添加后获取指向第一个向量的指针(是的,它需要两个循环)。

      【讨论】:

        猜你喜欢
        • 2011-10-01
        • 2011-02-11
        • 2010-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多