2013-05-21 3 views
4

У меня есть некоторые вопросы о неограниченных союзах и их применении на практике. Давайте предположим, что у меня есть следующий код:Неограниченное объединение на практике

struct MyStruct 
{ 
    MyStruct(const std::vector<int>& a) : array(a), type(ARRAY) 
    {} 
    MyStruct(bool b) : boolean(b), type(BOOL) 
    {} 
    MyStruct(const MyStruct& ms) : type(ms.type) 
    { 
     if (type == ARRAY) 
      new (&array) std::vector<int>(ms.array); 
     else 
      boolean = ms.boolean; 
    } 
    MyStruct& operator=(const MyStruct& ms) 
    { 
     if (&ms != this) { 
      if (type == ARRAY) 
       array.~vector<int>(); // EDIT(2) 
      if (ms.type == ARRAY) 
       new (&array) std::vector<int>(ms.array); 
      else 
       boolean = ms.boolean; 
      type = ms.type; 
     } 
     return *this; 
    } 
    ~MyStruct() 
    { 
     if (type == ARRAY) 
      array.~vector<int>(); 
    } 

    union { 
     std::vector<int> array; 
     bool    boolean; 
    }; 
    enum {ARRAY, BOOL} type; 
}; 
  1. этот код действителен :)?
  2. Нужно явно вызывать деструктор вектора каждый раз, когда мы используем логическое значение (как указано здесь http://cpp11standard.blogspot.com/2012/11/c11-standard-explained-1-unrestricted.html)
  3. Почему размещение нового требуется вместо того, чтобы просто делать что-то вроде «массив = ms.array»?

EDIT:

  • Да, он компилирует
  • «Члены, объявленные внутри анонимные объединения фактически члены содержащего класса, и можно инициализировать в конструкторе объемлющего класса.» (C++11 anonymous union with non-trivial members)
  • Добавление явных деструкторов, как предложено, приводит к SIGSEV с г ++ 4.8/Clang 4,2
+0

Союзы иногда используются, чтобы подорвать тип системы - пиннинг типа.В этом случае вы не будете уничтожать «вектор» перед чтением/записью 'bool', но стандарт не определяет (и не может) определить, что это означает - эффект зависит от деталей платформы. Вы, вероятно, не сделали бы этого с 'vector' в любом случае - возможно, с массивом char, чтобы увидеть байты. Если вы не пишите, вы можете только зачитывать, что было написано в последний раз (или ожидать повреждения данных), и вы должны удалить (уничтожить) все, что уже есть в союзе, прежде чем писать что-то еще (или ожидать утечки памяти/ресурсов) , – Steve314

ответ

2
  1. багги Кодекса: изменение array.clear(); в array.~vector<int>();

Объяснение: operator= использует размещение new над объектом, который не был разрушен, что может сделать что угодно, но практически можно ожидать утечки динамической памяти, использованной предыдущим массивом (clear() не освобождает m emory/change capacity, он просто разрушает элементы и изменения size).

От 9,5/2:

Если любой не статический член данных объединения имеет нетривиальное по умолчанию конструктор (12.1), конструктор копирования (12.8), переместите конструктор (12.8), копия оператор присваивания (12.8), переместить оператор присваивания (12.8) или деструктор (12.4), соответствующая функция-член объединения должна быть предоставлена ​​пользователем или она будет неявно удалена (8.4.3) для объединения.

Итак, конструктор vector, деструктор и т. Д. Никогда не запускаются сами по себе: вы должны называть их явно, когда захотите.

В 9.5/3 есть пример:

Рассмотрим следующий союз:

union U { 
    int i; 
    float f; 
    std::string s; 
}; 

С станд :: строка (21,3) объявляет нетривиальные версии всех специальные функции-члены, U будет иметь неявно удаленный конструктор по умолчанию, копировать/перемещать конструктор, оператор копирования/перемещения и деструктор. Чтобы использовать U, некоторые или все из этих функций-членов должны быть предоставлены пользователем.

Этот последний бит - «Для использования U некоторые или все эти функции-члены должны быть предоставлены пользователем». - кажется, предполагается, что U должен координировать свое собственное смутно ценностно-семантическое поведение, но в вашем случае это делает struct, поэтому вам не нужно определять какие-либо из этих функций-членов union.

2: мы должны вызывать деструктор массива всякий раз, когда значение массива заменяется логическим значением. Если в operator= новое значение массива будет размещаться вместо назначенного, то старый массив также должен иметь свой деструктор, но с использованием operator= было бы более эффективно, когда существующей памяти было бы достаточно для всех копируемых элементов. В принципе, вы должны соответствовать конструкциям и разрушениям. UPDATE: пример кода содержит ошибку в соответствии с вашим комментарием ниже.

3: Почему требуется размещение нового, а не просто что-то вроде «array = ms.array»?

массив = ms.array вызывает std::vector<int>::operator=, который всегда предполагает this указатель адреса уже правильно построенный объект. Внутри этого объекта вы можете ожидать, что там будет указатель, который будет либо NULL, либо ссылаться на некоторый внутренний буфер с короткой строкой, либо ссылаться на кучу. Если ваш объект не был разрушен, то operator= вполне может вызвать функцию освобождения памяти на фиктивном указателе. Размещение new говорит «игнорировать текущее содержимое памяти, которое будет занимать этот объект, и построить новый объект с действительными членами с нуля.

+0

Деструкция явно вектора в конструкторе boolean constructor/copy приводит к SIGSEV :(Мне не хватает чего-то здесь. Что касается анонимного члена профсоюза, он, по-видимому, разрешен стандартом (см. Http://stackoverflow.com/questions/10693913/c11-anonymous-union-with-non-trivial-members) – 3XX0

+0

@ 3XX0: извините - статья Википедии C++ 11 (http://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions) является немного вводящий в заблуждение в «// из-за члена Point, теперь требуется определение конструктора». - Я не должен был доверять ему, не проверяя стандарт. Обновили мой ответ соответственно и со стандартом, указанным выше, конструктором копирования не следует называть векторный деструктор - извините за рутину. –

+0

@ 3XX0: статья Википедии, упомянутая в моей ссылке выше, соответствует 9.5/3, как обсуждалось в моей статье - я бы сказал, что обе ошибочны в вашем случае. –

1

Объединение не объявляет конструктор по умолчанию, конструктор копирования, оператор присваивания копии или деструктор.

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

Если этот код неверен и его нельзя скомпилировать (это почти буква в букву в соответствии с примером в 9,5 пар. 3 стандарта, за исключением того, что это std::string, а не std::vector).
(не применяется для Anon союза, как правильно указал)

О вопросе (2): Для того, чтобы безопасно переключить соединение, это необходимо, да. В Стандарте явно указано, что в 9.5 пар. 4 [Примечание].
Это тоже имеет смысл, если вы думаете об этом. Максимум один элемент данных может быть активным в union в любое время, и они не магически по умолчанию построены/уничтожены, а это значит, что вам нужно правильно конструировать/уничтожить вещи. Не имеет смысла (или даже определено) использовать union как что-то другое в противном случае (не то, что вы не могли так или иначе, но это не определено).
Объект не является указателем, и вы не знаете, выделено ли оно в куче (даже если оно выделено в куче, тогда внутри еще один объект, поэтому его все еще не разрешено удалять). Как вы уничтожаете объект, если вы не можете позвонить delete? Как вы выделяете объект - возможно, несколько раз - без утечки, если вы не можете его удалить? Это не оставляет много вариантов. Поскольку [Замечание] имеет смысл.

+0

Поскольку это анонимный союз, вместо этого разрешено использовать объекты класса (http://stackoverflow.com/questions/10693913/c11-anonymous-union-with-non-trivial-members) Относительно вашего ответа (2) Я мог бы 't согласен больше, но разрушение вектора приводит к SIGSEV ... Даже последний фрагмент кода из узла segfault (clang 3.2/g ++ 4.8) – 3XX0

Смежные вопросы