обнуление производной структуры с использованием memset - PullRequest
1 голос
/ 16 августа 2011

Я хочу обнулить все члены производной структуры.

Существуют сотни членов, и время от времени добавляются новые, поэтому я чувствую, что их явная инициализация подвержена ошибкам.

Структуры не имеют виртуальных функций, и все поля-члены являются встроенными. Тем не менее, они не являются POD из-за наличия нетривиальных конструкторов.

Помимо стандартного недовольства на практике, вы видите какие-либо проблемы со следующим?

struct Base
{
    // Stuff
};

struct Derived : public Base
{
    // Hundreds of fields of different built-in types
    // including arrays

    Derived()
    {
        ::memset(reinterpret_cast<char*>this + sizeof (Base), 0, sizeof *this - sizeof (Base));
    }
};

Спасибо.

Ответы [ 4 ]

9 голосов
/ 16 августа 2011

Предполагается, что подобъект базового класса Base находится в начале Derived. Это не сработает, если вы добавите еще один базовый класс.

Кроме того, ваш код неверен: арифметика указателей выполняется в терминах объектов, а не в байтах. Вам нужно использовать reinterpret_cast<char*>(this) для выполнения арифметики в байтах. В любом случае, вы все равно не должны этого делать.

Рассмотрим следующий, не уродливый, соответствующий стандартам подход, использующий инициализацию значения:

struct Derived : public Base
{
    struct DerivedMembers { /* ... */ }

    DerivedMembers data;

    Derived() : data() { }
};

Пока DerivedMembers не имеет конструктора, это будет инициализировать значение каждого из элементов данных data, что выглядит так, как вы хотите.

Или, если вы хотите, чтобы члены были доступны без использования переменной-члена "data", рассмотрите возможность использования другого базового класса:

struct DerivedMembers { /* ... */ }

struct Derived : Base, DerivedMembers
{
    Derived() : DerivedMembers() { }
};
2 голосов
/ 16 августа 2011

Стандарт не "хмурится на практике"; неопределенное поведение .

Например:

this + sizeof (Base)

Ничто в стандарте C ++ не говорит о том, что это выражение разрешается в указатель на Derived. Действительно, так как тип this равен Derived * const, то вы сделали арифметику указателей. C ++ попытается добавить к нему, как если бы this был указателем на массив Derived, эквивалентный this[sizeof(Base)]. Что, вероятно, не то, что вы хотели.

Не ходите по темным коридорам неопределенного поведения, если вы не уверены, как сделать это правильно.

Самое главное, даже если вы нацелите гимнастику на работу, ваш код станет очень хрупким. Он может работать в этой версии вашего компилятора, но не получится в более поздней. Выполнение чего-то простого, такого как добавление виртуальной функции к Base, приведет к хаосу в вашем коде, так как вы уничтожите указатель vtable.

Мне кажется, у вас есть пара проблем:

  1. Ненужное деривация. Если у Base нет ничего виртуального, почему вы публично получаете от него информацию?
  2. Толстый интерфейс. Если вы видите, что у класса начало сотен членов, то, вероятно, это слишком много.
2 голосов
/ 16 августа 2011

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

2 голосов
/ 16 августа 2011

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

См. аналогичный вопрос :

...