C ++ Векторы объектов и указателей - PullRequest
2 голосов
/ 01 декабря 2010

Это надуманный пример, который иллюстрирует проблему, с которой я столкнулся.По сути, я создаю вектор объектов, затем вектор указателей на объекты, затем печатаю указатели и разыменованные объекты.

#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

Кажется, что он работает должным образом, за исключением первого значения, которое, вероятно, является некоторым мусором в памяти.Почему влияет только первое значение?Если мой код не работает, почему он сломан только для первого вставленного указателя?


Обновление: я компилирую это, используя g++ в Ubuntu.Что касается конкретно того, что я делаю, я создаю проход анализа компилятора.Объекты 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* |
----------------------                                                  -----------------------

Сначала проход вставляет InstRec с информацией о I1.Он также создает указатели на этот новый InstRec, проиндексированный по номеру регистра.Здесь на самом деле r0 - это вектор одного элемента, который указывает на InstRec для I1, поэтому, если когда-нибудь снова встретится с r0 в последующей инструкции, InstRec для I1 будет обновлен.

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, проход просматривает в структуре UpdatePtrs вектор r0 указателей, следует за каждым указателем на их запись InstRec и обновляет InstRec с новой информацией.

Надеюсь, это проясняет то, что я пытаюсь сделать.Я реализовал предложение, впервые предложенное @MerickOWA, об использовании векторных индексов InstRec, а не указателей InstRec (поскольку, как только InstRecs добавляются в массив, они никогда не перемещаются), и, похоже, теперь это работает.

Ответы [ 4 ]

6 голосов
/ 01 декабря 2010

То, что вы делаете, очень похоже на создание чего-то вроде этого:

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

И затем каждый раз, когда вы добавляете новый int в MyInts, вы получаете итератор и помещаете его в MyIntIters.

Вы не можете сделать это с vector, потому что итераторы могут стать недействительными в любое время, когда вы добавляете новый int в MyInts.

Таким образом, вся ваша структура нарушена.Вам нужно придумать совершенно новый дизайн.Сначала я спросил бы вас, почему вы хотите вектор итераторов (или указатели) для другого вектора.Это для сортировки?Индексация как-то?Что-то другое?безусловно, есть лучший способ сделать то, что вы пытаетесь сделать. То, что вы пытаетесь сделать, поможет определить , как сделать это.

РЕДАКТИРОВАТЬ:

После прочтения и перечитывания вашего обновления несколько раз,мне кажется, что вы пытаетесь создать вектор гловов.То есть буфер переменной длины, который имеет структурированные данные в начале и что-то еще после него.

Это довольно сложный бизнес, требующий динамического распределения.Однако вы не используете динамическое размещение - вы помещаете объекты в вектор по значению.Указатели на эти объекты будут меняться по мере изменения размера и перетасовки вектора.

Так что если это то, что вы пытаетесь сделать, вам нужно изменить все вокруг, чтобы создать свой блеск, используя new,и нажмите на указатель на вектор.Это открывает целый ящик неприятностей Пандоры все же.Насколько велики вы делаете буферы?Как вы разбираете глом?Как вы обрабатываете глубокие копии, изменения размера и т. Д.?Как правильно избавиться от мух, не просачиваясь, как сито?Как я уже сказал, сложное дело.В моей работе мы все время так поступаем, и у нас есть куча стандартных практик, которые мы используем.Это заняло много усилий и много испытаний с методом проб и ошибок, и мы все еще находим проблемы.Если это то, что вы делаете, вы можете пойти другим путем.

4 голосов
/ 01 декабря 2010

Он не просто сломан на первом, а только один раз поврежден. Если вы заметили, указатели с 0 по 7 указывают на разные куски памяти, когда они должны быть рядом друг с другом.

std :: vector может перераспределить пространство, необходимое для динамических объектов, в любое время, когда вы добавляете что-то в вектор, и у него недостаточно емкости. Нельзя полагаться на то, что указатели (или итераторы) остаются действительными после добавления или вставки чего-либо, что может увеличить его размер.

Theres много решений, таких как

a) вы можете зарезервировать пространство (используя vector :: reserve) заблаговременно, чтобы такого динамического перераспределения не происходило, но вы должны знать максимальное количество объектов, которое вы добавите.

б) подождать, пока все объекты будут добавлены, прежде чем получить указатели на объекты

c) использовать индекс объекта и указатель на вектор в виде пары, чтобы ссылаться на ваши объекты в противоположность указателям на объекты напрямую. Это больше работы, но не изменится (при условии, что вы не вставляете / удаляете объекты в начале или в середине)

d) попытаться определить, когда вектор перераспределил свои данные (vector ::acity возвращает другое значение), очистить вектор-указатель и восстановить его

e) использовать другой контейнер для объектов, который не перераспределяется при изменениях, таких как std :: list, и отказываться от произвольного доступа к вашему контейнеру базовых объектов, вы все равно можете использовать вектор указателей, но теперь указатели не становятся аннулированной. (Вам действительно нужен произвольный доступ и указатели на объекты?)

е) переосмыслить свой дизайн, чтобы не требовать указателей

1 голос
/ 01 декабря 2010

Вы пытаетесь сохранить указатели на содержимое вектора. Это не работает, потому что вектор предназначен для перемещения его содержимого в памяти для реализации изменения размера.

В зависимости от того, что вам нужно, вы можете:

  • хранить индексы в векторе вместо

  • изобрел какой-то другой вид "ручки"

  • изменить вектор для хранения интеллектуальных указателей на экземпляры (которые будут выделены с помощью 'new' и существовать отдельно), а затем сохранить другой интеллектуальный указатель на тот же экземпляр. Интеллектуальные указатели будут поддерживать счетчик ссылок и освобождать память, как только экземпляры больше не будут указываться ни вектором, ни внешним кодом. (Конечно, для этого существуют готовые решения: см., Например, boost::shared_ptr.)

0 голосов
/ 01 декабря 2010

push_back делает недействительными итераторы и указатели на вектор. Что происходит:

  1. Вы нажимаете на TheInts
  2. Затем вы берете указатель на только что нажатый элемент и сохраняете его в другом векторе

Перемещение в точке 1 выше может перераспределить вектор и лишить законной силы все указатели, ранее взятые на предшествующие элементы. Значение, на которое они указывают, находится вне вашего контроля.

Почему это происходит только один раз - просто удача (вектор может выделяться большими кусками, чем необходимо в данный момент).

Используйте dequeue или перечислите или зарезервируйте необходимое пространство заранее (если вы можете быть уверены, сколько вам нужно) или получите указатели на первый вектор после того, как вы закончили добавлять к нему (да, для этого нужны два цикла).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...