Инициализация и поддержка структур структур - PullRequest
3 голосов
/ 31 июля 2011

Я пишу код на C ++ для работы с кучей гистограмм, которые заполняются из лабораторных измерений. Я сталкиваюсь с проблемами, когда пытаюсь лучше организовать вещи, и думаю, что мои проблемы возникают из-за неправильного обращения с указателями и / или структурами.

Мой оригинальный дизайн выглядел примерно так:

// the following are member variables
Histogram *MassHistograms[3];
Histogram *MomentumHistograms[3];
Histogram *PositionHistograms[3];

, где элемент 0 каждого массива соответствовал одному лабораторному измерению, элемент 1 каждого массива соответствовал другому и т. Д. Я мог получить доступ к отдельным гистограммам через MassHistograms[0] или подобное, и это работало хорошо. Однако организация мне показалась неправильной - если бы я должен был выполнить новое измерение, мне пришлось бы добавить элемент к каждому массиву гистограмм. Вместо этого я придумал

struct Measurement {
    Histogram *MassHistogram;
    Histogram *MomentumHistogram;
    Histogram *PositionHistogram;
};

Как дополнительный уровень сложности, я также хотел связать эти измерения в соответствии с обработкой, которая была сделана для их данных, поэтому я сделал

struct MeasurementSet {
    Measurement SignalMeasurement;
    Measurement BackgroundMeasurement;
};

Я думаю, что это расположение гораздо более логично и расширяемо, но оно не работает ;-) Если у меня есть код, подобный

MeasurementSet ms;
Measurement m = ms.SignalMeasurement;
Histogram *h = m.MassHistogram;

и затем попытайтесь что-то сделать с h, я получаю ошибку сегментации. Поскольку аналогичный код раньше работал нормально, я предполагаю, что не правильно обрабатываю структуры в своем коде. В частности, нужно ли каким-либо образом явно инициализировать структуры? (Histogram предоставлены чужой библиотекой, и простого объявления Histogram *SomeHistograms[4] было достаточно, чтобы инициализировать их раньше.)

Я ценю обратную связь. Я неплохо знаком с Python и Clojure, но мои ограниченные знания C ++ не распространяются на [что похоже на] тайну ухода и кормления структур: -)


Что я в итоге сделал

Я превратил Measurement в полноценный класс:

class Measurement {
    Measurement() {
        MassHistogram = new Histogram();
        MomentumHistogram = new Histogram();
        PositionHistogram = new Histogram();
    };

    ~Measurement() {
        delete MassHistogram;
        delete MomentumHistogram;
        delete PositionHistogram;
    };

    Histogram *MassHistogram;
    Histogram *MomentumHistogram;
    Histogram *PositionHistogram;
}

(универсальный конструктор Histogram(), который я вызываю, работает нормально.) Другая проблема, с которой я столкнулся, была решена путем передачи Measurement s по ссылке; в противном случае деструктор будет вызван в конце любой функции, получившей Measurement, и следующая попытка что-либо сделать с одной из гистограмм приведет к сбою.

Спасибо всем за ответы!

Ответы [ 4 ]

1 голос
/ 31 июля 2011

Во-первых, всегда помните, что структуры и классы - это почти одно и то же.Единственное отличие состоит в том, что члены структуры по умолчанию являются открытыми, а член класса по умолчанию закрытым.Но все остальное точно так же.

Во-вторых, тщательно различайте указатели и объекты.

Если я напишу

Histogram h;

пробел дляДанные гистограммы будут распределены, и будет вызван ее конструктор.(Конструкция - это метод с тем же именем, что и у класса, здесь Historgram ())

Если я напишу

Histogram* h;

Я объявляю переменную из 32/64 битов, котораябудет использоваться как указатель на память.Он инициализируется случайным значением.Опасно!

Если я напишу

Histogram* h = new Histogram();

, то для одного элемента данных гистограммы будет выделена память и будет вызван ее конструктор.Адрес в памяти будет сохранен в «h».

Если я напишу

Histogram* copy = h;

Я снова объявляю 32/64-битную переменную, которая указывает на точно такой же адрес в памятикак h

Если я напишу

Histogram* h = new Historgram;
Histogram* copy = h;
delete h;

происходит следующее

  1. память выделяется для объекта гистограммы
  2. Конструктор гистограммы будетбыть вызванным (даже если вы не написали его, ваш компилятор сгенерирует его).
  3. h будет содержать адрес памяти этого объекта
  4. оператор удаления вызовет деструктор гистограммы (даже если вы не написали его, ваш компилятор сгенерирует его).
  5. память, выделенная для объекта гистограммы, будет освобождена
  6. копия будет по-прежнему содержать адрес памяти, по которому объект использовался длявыделяться.Но вы не можете использовать его.Это называется «висячий указатель»
  7. h будет неопределенным

Короче говоря: «n.MassHistogram» в вашем коде ссылается на случайную область в памяти.Не используйте это.Либо выделите его сначала с помощью оператора «new», либо объявите его как «гистограмму» (объект вместо указателя)

Добро пожаловать в CPP: D

1 голос
/ 31 июля 2011

Вам известно, что ваше определение Measurement не выделяет память для фактических Histogram с? В вашем коде m.MassHistogram является висящим (неинициализированным) указателем, он не указывает ни на измеренный Histogram, ни на какую-либо память, способную хранить Histogram. Как только что @Nari Rennlos опубликовал, вам нужно указать на существующий (или вновь выделенный) Histogram.

Как выглядит интерфейс вашей сторонней библиотеки? Если это вообще возможно, у вас должно быть Measurement, содержащее 3 Histogram с (в отличие от 3 указателей на Histogram с). Таким образом, когда вы создаете Measurement или MeasurementSet, соответствующие Histogram будут созданы для вас, и то же самое относится и к уничтожению. Если вам все еще нужен указатель, вы можете использовать оператор &:

struct Measurement2 {
    Histogram MassHistogram;
    Histogram MomentumHistogram;
    Histogram PositionHistogram;
};

MeasurementSet2 ms;
Histogram *h = &ms.SignalMeasurement.MassHistogram; //h valid as long as ms lives

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

MeasurementSet ms;                    //6 uninitialized pointers to Histograms
Measurement m = ms.SignalMeasurement; //3 more pointers, values taken from first 3 above
Histogram *h = m.MassHistogram;       //one more pointer, same uninitialized value

Хотя, если бы указатели были инициализированы, все 10 из них указывали бы на фактический Histogram на данный момент.

Хуже, если у вас есть действительные члены вместо указателей:

MeasurementSet2 ms;                    //6 Histograms
Measurement2 m = ms.SignalMeasurement; //3 more Histograms, copies of first 3 above
Histogram h = m.MassHistogram;         //one more Histogram

h.firstPoint = 42;
m.MassHistogram.firstPoint = 43;
ms.SignalMeasurement.MassHistogram.firstPoint = 44;

... теперь у вас есть 3 слегка отличающиеся гистограммы массовых сигналов, 2 пары идентичных гистограмм импульса и сигнала положения и триплет фоновых гистограмм.

1 голос
/ 31 июля 2011

Вы уверены, что Histogram *SomeHistograms[4] инициализировал данные? Как вы заполняете структуры гистограммы?

Проблема здесь не столько в структурах, сколько в указателях, которые сбивают вас с толку. Когда вы делаете это: MeasurementSet ms; он объявляет «автоматическую переменную» типа MeasurementSet. Это означает, что вся память для MeasurementSet «выделена» и готова к работе. MeasurementSet, в свою очередь, имеет две переменные типа Measurement, которые также «выделены» и «готовы к работе». Измерение, в свою очередь, имеет 3 переменные типа Гистограмма *, которые также «выделены» и «готовы к работе» ... но подождите! Тип «Гистограмма *» является «указателем». Это означает, что это адрес - 32- или 64-битное (или любое другое) значение, которое описывает фактическую ячейку памяти. И это все. Это зависит от вас, чтобы указать на что-то - поместить что-то в этом месте. Прежде чем он укажет на что-либо, он будет иметь буквально случайные данные (или 0'данных данных, или некоторые специальные отладочные данные, или что-то в этом роде) - дело в том, что если вы попытаетесь что-то с этим сделать, вы получить ошибку сегментации, потому что вы, вероятно, будете пытаться прочитать часть данных, которые ваша программа не должна читать.

В c ++ структура - это почти то же самое, что и класс (который имеет похожую концепцию в python), и вы обычно выделяете его следующим образом:

m.MassHistogram = new Histogram();

... после этого гистограмма готова к работе. Тем не менее, YMMV: вы можете выделить один самостоятельно? Или вы можете получить только один из какой-то библиотеки, может быть, из устройства чтения и т. Д.? Кроме того, хотя вы можете делать то, что я написал, это не обязательно «красиво». C ++ - ic решение было бы поместить размещение в конструктор (например, init в python) и удалить в деструкторе.

1 голос
/ 31 июля 2011

Когда ваша структура содержит указатель, вы должны инициализировать эту переменную самостоятельно.Пример </p> <pre><code>struct foo { int *value; }; foo bar; // bar.value so far is not initialized and points to a random piece of data bar.value = new int(0); // bar.value now points to a int with the value 0 // remember, that you have to delete everything that you new'd, once your done with it: delete bar.value;

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