У меня здесь очень своеобразная ситуация ... Я унаследовал какой-то старый код C++ (pre-C++ 11), который длинный, сложный, и половина его написана с помощью C и другая половина с менталитетом C-с-классами (то есть: классы с большим количеством членов данных, не слишком много методов класса, прямая манипуляция данными ... определенно этот проект нуждается в некотором рефакторинге, вот что я делаю правильно Теперь). Но код раскрывает некоторые проблемы, которые я нахожу загадочными.Может ли экземпляр класса C++ изменить его размер во время выполнения
Для начала давайте рассмотрим следующую очень простые (бесплатныеошибки) ситуации:
#include <iostream>
static int c = 0;
struct Bar
{
Bar() : base_id(++c) { std::cout << "Bar "<< base_id << std::endl;}
int base_id;
};
struct Foo : public Bar
{
Foo() : x(c) { std::cout << "Foo "<< x << std::endl;}
int x;
};
int main()
{
Bar* b = new Foo[200];
Foo *p;
for(p = (Foo*)b; p - (Foo*)b < 200; p ++)
{
std::cout << p->base_id << " " << ((Foo*)p)->x
<< " p-b=" << (unsigned)(p-(Foo*)b) << std::endl;
}
delete[] b;
}
или текстовую версию: мы создаем массив объектов базового класса через new Derived
. Насколько это хорошо, это то, что делает большое сложное приложение (там классы более иерархичны и конструкторы делают больше), но глобальная статическая переменная (c
) присутствует также в большом сложном приложении, она использует ее как уникальный объект идентификаторы. Затем начинает работать большое сложное приложение и т. Д.
Затем в некоторый момент времени мы перебираем массив объектов, выполняя некоторую работу. Итерация выглядит точно так же, как и тот, который я написал здесь, найденный в цикле for
с исчерпывающей арифметикой указателя. Мой пример здесь просто печатает идентификатор объекта (m
) в большом сложном приложении.
Но где-то в большом сложном приложении происходит какая-то магия ... В какой-то момент после половины списка объекты (полученные с помощью указателя p
) больше не действительны, их base_id
s покажут данные, которые мне нравятся много похоже на указатели и другие значения элементов данных. Однако, если я проверю элементы массива на конкретном индексе, значение будет действительным (т. Е. Правильно увеличивая идентификаторы объектов).
Я также проверил следующее:
- Там, кажется, нет никакой проблемы повреждения памяти. Валгринд и детектив памяти Клана не показывают никаких потаенных вещей.
- Все объекты правильно созданы и уничтожены, нет утечек памяти.
- Единственное место, где это сумасшествие происходит в этом цикле выше
Итак ... Я пришел к одному выводу:
- кто-то изменяет некоторые значения массива на самом деле указывают на другой тип объект (все они получены из
Bar
(e ... несколько иначе названных базовых классов), поэтому, возможно, мне нужно будет больше копать в коде, чтобы найти это. Но это огромная база кода, есть буквально тысячи ссылок для этот массив и до этого я хотел бы задать вопрос:
(И здесь возникает вопрос :)
Я не знаю, что C объект ++ может изменить его размер во время выполнения (если это возможно, пожалуйста, поделитесь им с нами), так что у возможного вопроса у меня есть что кто-нибудь в сообществе имеет какие-либо идеи, почему эта арифметика указателя ведет себя так странно?
(да, проект будет преобразован в соответствующий C++ 11 со стандартными контейнерами и т. Д.).но сейчас я просто заинтересован в этом конкретном вопросе)
Размер объекта рассчитывается во время компиляции. Он не может быть изменен во время выполнения. – teivaz
«До сих пор так хорошо» - нет, ты уже довольно напортачил. См. Здесь: http://stackoverflow.com/questions/6171814/why-is-it-undefined-behavior-to-delete-an-array-of-derived-objects-via-a-base – Mat
, если только классы строго стандартная компоновка, (Foo *) b не обязательно приведет к тому же значению (адресу), что и b. Может ли это иметь значение? –