Инициализация объединения с нетривиальным конструктором - PullRequest
52 голосов
/ 26 ноября 2008

У меня есть структура, в которой я создаю собственный конструктор для инициализации членов в 0. В старых компиляторах я видел, что в режиме выпуска без установки memset в 0 значения не инициализируются.

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

Итак, вопрос 1. Гарантирует ли реализованный компилятором по умолчанию конструктор, что все члены структуры будут инициализированы нулем? Нетривиальный конструктор просто устанавливает memset всех членов в '0', чтобы обеспечить чистую структуру.

Вопрос 2: Если в базовой структуре должен быть указан конструктор, как может быть реализовано объединение, содержащее этот элемент и обеспечивающее 0 инициализированных базовых элементов?

Ответы [ 6 ]

46 голосов
/ 26 ноября 2008

Вопрос 1: Конструкторы по умолчанию инициализируют члены POD равными 0 в соответствии со стандартом C ++. Смотри приведенный ниже текст.

Вопрос 2. Если конструктор должен быть указан в базовом классе, этот класс не может быть частью объединения.

Наконец, вы можете предоставить конструктор для вашего объединения:

union U 
{
   A a;
   B b;

   U() { memset( this, 0, sizeof( U ) ); }
};

Для Q1:

Из C ++ 03, 12.1 Конструкторы, стр 190

Неявно определенный конструктор по умолчанию выполняет набор инициализаций класс, который будет выполняться пользовательским конструктором по умолчанию для этого класса с пустым списком mem-initializer-list (12.6.2) и пустым телом функции.

Из C ++ 03, 8.5 Инициализаторы, стр. 145

По умолчанию инициализировать объект типа T означает:

  • если T не тип класса POD (раздел 9), конструктор по умолчанию для Т называется (а инициализация плохо сформирована, если T не имеет доступного по умолчанию конструктор);
  • если T является типом массива, каждый элемент инициализируется по умолчанию;
  • в противном случае объект инициализируется нулями .

Инициализация нуля объекта типа T означает:

  • если T - скалярный тип (3.9), объекту присваивается значение 0 (ноль), преобразованное в T;
  • если T является типом класса, не являющимся объединением, каждый нестатический член данных и каждый подобъект базового класса инициализируются нулями ;
  • если T является типом объединения, первый именованный элемент данных объекта инициализируется нулями;
  • если T является типом массива, каждый элемент инициализируется нулями;
  • если T является ссылочным типом, инициализация не выполняется.

Для Q2:

Из C ++ 03, 12.1 Конструкторы, стр 190

Конструктор является тривиальным, если он является неявно объявленным конструктором по умолчанию и если:

  • его класс не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1), а
  • все прямые базовые классы этого класса имеют тривиальные конструкторы, а
  • для всех нестатических членов данных своего класса, которые имеют тип класса (или массив) из них) каждый такой класс имеет тривиальный конструктор

Из C ++ 03, 9,5 Союзов, стр. 162

Объединение может иметь функции-члены (включая конструкторы и деструкторы), но не виртуальные (10.3) функции. У союза не должно быть базовых классов. Объединение не должно использоваться в качестве базового класса. Объект класса с нетривиальным конструктором (12.1), нетривиальным конструктором копирования (12.8), нетривиальным деструктором (12.4) или нетривиальным оператор копирования (13.5.3, 12.8) не может быть членом объединения, а также не может содержать массив таких объектов

31 голосов
/ 22 октября 2015

В C ++ все изменилось в лучшую сторону 11.

Теперь вы можете сделать это легально, как , описанный самим Страуструпом (я получил эту ссылку из статьи Википедии на C ++ 11 ).

Пример в Википедии выглядит следующим образом:

#include <new> // Required for placement 'new'.

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p; // Illegal in C++03; legal in C++11.
    U() {new(&p) Point();} // Due to the Point member, a constructor
                           // definition is now *required*.
};

Страуструп углубляется в детали.

3 голосов
/ 26 ноября 2008

Члены профсоюза AFAIK не могут иметь конструкторов или деструкторов.

Вопрос 1: нет, такой гарантии нет. Любой POD-член, которого нет в списке инициализации конструктора, инициализируется по умолчанию, но это с указанным вами конструктором и имеет список инициализатора. Если вы не определяете конструктор или определяете конструктор без списка инициализаторов и пустого тела, POD-члены не будут инициализированы.

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

Вопрос 2: вы всегда можете инициализировать структуры / объединения следующим образом:

struct foo
{
    int a;
    int b;
};

union bar
{
    int a;
    foo f;
};

bar b = { 0 };
2 голосов
/ 26 ноября 2008

Как упоминалось в комментарии Грега Роджерса к сообщению unwesen , вы можете дать своему объединению конструктор (и деструктор, если хотите):

struct foo
{
    int a;
    int b;
};

union bar
{
    bar() { memset(this, 0, sizeof(*this)); }

    int a;
    foo f;
};
0 голосов
/ 26 ноября 2008

Вам нужно будет дождаться, пока компиляторы поддержат C ++ 0x, чтобы получить это. До тех пор, извините.

0 голосов
/ 26 ноября 2008

Можете ли вы сделать что-то подобное?

class Outer
{
public:
    Outer()
    {
        memset(&inner_, 0, sizeof(inner_));
    }
private:
    union Inner
    {
        int qty_;
        double price_;
    } inner_;
};

... или что-то в этом роде?

union MyUnion
{
    int qty_;
    double price_;
};

void someFunction()
{
    MyUnion u = {0};
}
...