Подтвердить что ты не робот

Является ли хорошей практикой использование союзов в С++?

Мне нужно определить класс следующим образом:

class Color
{
private:
   union Data
   {
       unsigned int intValue;
       unsigned char argbBytes[4];
   }

private:
    Data m_data;
};

Другим способом является, конечно, определение данных как целого и приведение его в массив char, когда это необходимо.

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

4b9b3361

Ответ 1

Союзы могут быть точными, если вы используете их тщательно.

Их можно использовать двумя способами:

  • Чтобы разрешить доступ к одному типу данных несколькими способами (как в вашем примере, доступ к цвету как int или (как вы, вероятно, предполагали) четыре символа)

  • Сделать полиморфный тип (одно значение, которое может содержать int или float, например).

Случай (1) Прекрасен, потому что вы не меняете значение типа - вы можете читать и писать любого из членов профсоюза, не нарушая ничего. Это делает его очень удобным и эффективным способом доступа к тем же данным в несколько разных формах.

Случай (2) может быть полезен, но чрезвычайно опасен, потому что вам нужно всегда получать доступ к правильному типу данных из союза. Если вы пишете int и попытаетесь прочитать его как float, вы получите бессмысленное значение. Если использование памяти не является вашим основным соображением, может быть лучше использовать простую структуру с двумя членами в ней.

Союзы были жизненно важны для C. На С++ обычно есть гораздо более приятные способы достижения одних и тех же целей (например, класс может использоваться для обертывания значения и позволяет ему получать доступ по-разному). Однако, если вам нужна сырая производительность или ситуация с критической памятью, профсоюзы могут по-прежнему быть полезным подходом.

Ответ 2

Это хорошая практика? Да, но с некоторыми оговорками.

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

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

Ответ 3

В С++ использование союзов ограничено тем фактом, что их члены должны быть POD (простые старые данные). Например, член союза не может иметь конструктор или деструктор, среди других ограничений.

Ответ 4

Использование профсоюзов по-прежнему является приемлемой практикой. Просто измените rgbBytes на массив:)

В C объединения могут использоваться для разных целей. Иногда они используются как тип Variant, т.е. Для хранения значений различного типа в одном и том же месте памяти. Это использование было бы сомнительным в С++, потому что вы бы использовали наследование/полиморфизм. Однако другое использование профсоюзов заключается в предоставлении разных "интерфейсов" к тем же данным. Этот вид использования по-прежнему действителен для С++.

Ответ 5

Поскольку вы используете С++, я бы сказал, что это не очень хорошая практика. Если вы ограничены чисто C, уверен, почему бы и нет.

Самая большая проблема imo в том, что размер объединения всегда является размером с наибольшим "членом", поэтому, если вы хотите сохранить байт или shitloadofdata, размер sizeof (shitloadofdata), а не байт.

Полиморфизм - намного лучший вариант, чем союзы.

Ответ 6

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

Однако это не широко распространенная рекомендация, и Саттер и Александреску не выносили против них в своих С++ Coding Standards, насколько это возможно как я помню.

К счастью, все, кого я знаю, находят их настолько трудными, чтобы понять, что они их не производят. Если бы только они обнаружили, что void * в API слишком трудно получить правильно:)

Ответ 7

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

Boost:: variant решает многие из этих проблем. Как указывает документация, объединение "почти бесполезно в объектно-ориентированной среде", а boost:: variant дает очень объектно-ориентированный подход к решению практических проблем объединения. Этот интерфейс разработан, чтобы не разрешать доступ к варианту, если вы не используете соответствующий тип, а пример шаблона "посетителя", который они предоставляют, дает ошибки времени компиляции, если объединение расширено, чтобы включить тип, которого вы не ожидали.

Что касается, если это полезно; Я думаю так. Я использовал их для просто больших интерфейсов

 class some_xml_class {
 public:
    void set_property(const string&, const string&);
    void set_property(const string&, const vector<string>&);
    void set_property(const string&, const set<string>&);
    void set_property(const string&, int);

    void set_super_property(const string&, const string&);
    void set_super_property(const string&, const vector<string>&);
    void set_super_property(const string&, const set<string>&);
    void set_super_property(const string&, int);

стихи

 class some_xml_class {
 public:
    typedef boost::variant<string, vector<string>, set<string>, int> property_type;
    void set_property(const string&, const property_type&);
    void set_super_property(const string&, const property_type&);

(шаблоны также могут быть полезны здесь, но пусть говорят, что имп был достаточно длинным, я не хотел его встроить)

Ответ 8

Этот код будет иметь поведение undefined, если используется так, как вы описываете.

В С++ только один последний член объединения активен в любой момент времени. Доступ к другим членам - это доступ к неинициализированным переменным.

Для подробного обсуждения см. этот поток.

Союзы могут быть использованы для экономии места; например реализуя вариант. Они не могут использоваться для кастомизации типов.

Ответ 9

Это абсолютно допустимое использование объединений в С++. В зависимости от того, что вы хотите сделать, вы можете изменить свой класс на союз, чтобы у вас не было гнездования. Союзы могут иметь методы и использовать наследование. Если это невозможно (есть другие члены данных), вы можете использовать анонимный союз следующим образом:

class Color
{
private:
   union
   {
       unsigned int intValue;
       unsigned char argbBytes[4];
   };
public:
    unsigned int GetIntValue() { return intValue; }
};

Ответ 10

Да, это определенно хорошая практика использования профсоюзов - это единственный способ сообщить компилятору, что кусок памяти используется для хранения разных типов. Использование союза поддерживает безопасность статического типа, поскольку не требуется использовать reinterpret_cast < > и упрощает чтение кода.

Также необходимо использовать объединения при компиляции со строгим псевдонимом, и в этом случае компилятор предположит, что указатели на разные типы никогда не будут указывать на одну и ту же память. Строгое сглаживание - это тема сама по себе, но при коротком чтении/записи в одну и ту же память с помощью разных типов указателей, когда включен режим строгого сглаживания, часто не будет вести себя так, как ожидалось.

Ответ 11

Если вы используете GCC и переходите к указателям разыменования, ссылающимся на одно и то же местоположение, лучше придерживаться союзов.

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

int main() {
    char rgba[4] = {0xCC, 0xCC, 0xCC, 0};
    int *value = (int*)&rgba;
}

Компиляция этого кода с помощью -Wstrict-aliasing = 2 приведет к предупреждению о том, что были нарушены строгие правила псевдонимов. Доступ к значению - undefined. С другой стороны, использование соединения для доступа к некоторой части другой переменной не является нарушением строгих правил псевдонимов.