Безопасен этот прием инициализации структуры C++?

Похоже, он не подписывает ваш код.

/Users/user/Library/Developer/Xcode/DerivedData/Mission_Objective-hiufpyxwvsqqucfybrbffprxvtju/Build/Products/Debug-iphoneos/Mission Objective.app/Frameworks/libswiftCoreAudio.dylib: code object is not signed at all

Убедитесь, что вы установили команду в настройках проекта в разделе «Подписание».

31
задан Rob 21 September 2008 в 20:46
поделиться

15 ответов

ПРЕАМБУЛА:

, В то время как мой ответ все еще в порядке, я нахожу ответ litb вполне выше моего потому что:

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

Поэтому, рассмотрите ответ litb перед моим. На самом деле я предлагаю, чтобы автор вопроса рассмотрел ответ litb как правильный.

Исходный ответ

Помещение истинного объекта (т.е. станд.:: строка), и т.д. внутри повредится, потому что истинный объект будет инициализирован перед memset, и затем, перезаписан, обнуляет.

Используя список инициализации не работает на g ++ (я удивлен...). Инициализируйте его вместо этого в теле конструктора CMyStruct. Это будет дружественный C++:

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct() { n1 = 0 ; n2 = 0 ; }
};

P.S.: Я предположил, что Вы действительно имели никакой управление MY_STRUCT, конечно. С управлением Вы добавили бы конструктора непосредственно в MY_STRUCT и забыли бы о наследовании. Обратите внимание, что можно добавить невиртуальные методы к подобной C структуре, и все еще иметь его, ведут себя как структура.

РЕДАКТИРОВАНИЕ: Добавленная недостающая круглая скобка, после комментария Lou Franco.Спасибо!

РЕДАКТИРОВАНИЕ 2: Я попробовал код g ++, и по некоторым причинам, использование списка инициализации не работает. Я исправил код с помощью конструктора тела. Решение все еще допустимо, все же.

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

РЕДАКТИРОВАНИЕ 3: После чтения комментария Rob я предполагаю, что у него есть точка, достойная обсуждения: "Согласованный, но это могло быть огромной структурой Win32, которая может измениться с новым SDK, таким образом, memset соответствует требованиям завтрашнего дня".

я не соглашаюсь: Зная Microsoft, это не изменится из-за их потребности в идеальной обратной совместимости. Они создадут вместо этого расширенный MY_STRUCT Исключая структура с тем же начальным расположением как MY_STRUCT, с дополнительными участниками в конце, и распознаваемый через членскую переменную "размера" как структура, используемая для RegisterWindow, IIRC.

, Таким образом, единственный актуальный вопрос, остающийся от комментария Rob, является "огромной" структурой. В этом случае возможно, memset более удобен, но необходимо будет сделать MY_STRUCT переменным членом CMyStruct вместо того, чтобы наследоваться ему.

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

РЕДАКТИРОВАНИЕ 4: смотрите на решение Frank Krueger. Я не могу обещать, что это портативно (я предполагаю, что это), но это все еще интересно с технической точки зрения, потому что это показывает один случай, куда в C++ "этот" указатель "адрес" перемещается от его базового класса до его наследованного класса.

19
ответ дан 27 November 2019 в 21:23
поделиться

Это заставило бы меня чувствовать себя намного более в безопасности, поскольку это должно работать, даже если существует vtable (или компилятор будет кричать).

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

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

9
ответ дан 27 November 2019 в 21:23
поделиться

Намного лучше, чем memset, можно использовать этот небольшой прием вместо этого:

MY_STRUCT foo = { 0 };

Это инициализирует всех участников к 0 (или их значение по умолчанию iirc), никакая потребность к specifiy значение для каждого.

12
ответ дан 27 November 2019 в 21:23
поделиться

С точки зрения C++ ISO существует две проблемы:

(1) объект POD? Акроним обозначает Простые Данные, и стандарт перечисляет то, что Вы не можете иметь в POD (Википедия имеет хорошую сводку). Если это не POD, Вы не можете memset это.

(2) там участники, для которых все-нуль битов недопустим? В Windows и Unix, Нулевой указатель является всем нулем битов; это не должно быть. Плавающая точка 0 имеет весь нуль битов в IEEE754, который довольно распространен, и на x86.

подсказка Frank Kruegers обращается к Вашим проблемам путем ограничения memset основой POD класса не-POD.

1
ответ дан 27 November 2019 в 21:23
поделиться

Попробуйте это - новая перегрузка.

РЕДАКТИРОВАНИЕ: Я должен добавить - Это безопасно, потому что память обнуляется, прежде любой конструкторов вызывают. Большой дефект - только работает, если объект динамично выделяется.

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        // whatever
    }
    void* new(size_t size)
    {
        // dangerous
        return memset(malloc(size),0,size);
        // better
        if (void *p = malloc(size))
        {
            return (memset(p, 0, size));
        }
        else
        {
            throw bad_alloc();
        }
    }
    void delete(void *p, size_t size)
    {
        free(p);
    }

};
1
ответ дан 27 November 2019 в 21:23
поделиться

То, что я делаю, использовать совокупную инициализацию, но только определение инициализаторов для участников я забочусь о, например:

STARTUPINFO si = {
    sizeof si,      /*cb*/
    0,              /*lpReserved*/
    0,              /*lpDesktop*/
    "my window"     /*lpTitle*/
};

остающиеся участники будут инициализированы к нулям соответствующего типа (как в сообщении Drealmer). Здесь, Вы доверяете Microsoft для не бесплатного повреждения совместимости путем добавления новых элементов структуры в середине (разумное предположение). Это решение кажется мне оптимальный - один оператор, никакие классы, никакой memset, никакие предположения о внутреннем представлении нулевых или нулевых указателей с плавающей точкой.

я думаю, что взломы, включающие наследование, являются ужасным стилем. Общедоступное наследование значит ISA для большинства читателей. Обратите внимание также, что Вы наследовались классу, который не разработан, чтобы быть основой. Как нет никакого виртуального деструктора, клиенты, которые удаляют экземпляр производного класса через указатель на основу, вызовут неопределенное поведение.

0
ответ дан 27 November 2019 в 21:23
поделиться

Это - немного кода, но это является допускающим повторное использование; включайте его однажды, и это должно работать на любой POD. Можно передать экземпляр этого класса к любой функции, ожидая MY_STRUCT или использовать функцию GetPointer для передачи его в функцию, которая изменит структуру.

template <typename STR>
class CStructWrapper
{
private:
    STR MyStruct;

public:
    CStructWrapper() { STR temp = {}; MyStruct = temp;}
    CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {}

    operator STR &() { return MyStruct; }
    operator const STR &() const { return MyStruct; }

    STR *GetPointer() { return &MyStruct; }
};

CStructWrapper<MY_STRUCT> myStruct;
CStructWrapper<ANOTHER_STRUCT> anotherStruct;

Таким образом, Вы не должны волноваться о том, являются ли ПУСТЫЕ УКАЗАТЕЛИ всем 0 или представлениями с плавающей точкой. Пока STR является простым составным типом, вещи будут работать. Когда STR не будет простым составным типом, Вы получите ошибку времени компиляции, таким образом, Вы не должны будете волноваться о случайном неправильном использовании этого. Кроме того, если тип содержит что-то более сложное, пока это имеет конструктора по умолчанию, Вы в порядке:

struct MY_STRUCT2
{
    int n1;
    std::string s1;
};

CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";

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

0
ответ дан 27 November 2019 в 21:23
поделиться

Мое мнение нет. Я не уверен, что это получает также.

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

Создают конструктора для CMyStruct, который берет MyStruct, имеет параметр.

CMyStruct::CMyStruct(MyStruct &)

Или что-то вроде, которое искало. Можно затем инициализировать общедоступного или частного члена 'MyStruct'.

1
ответ дан 27 November 2019 в 21:23
поделиться

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

, Вероятно, это работает, пока Вы не находите компилятор, на котором это не делает, или Вы бросаете некоторых vtable в соединение.

2
ответ дан 27 November 2019 в 21:23
поделиться

Примеры имеют "неуказанное поведение".

Для не-POD, порядок, согласно которому компилятор размечает объект (все классы оснований и участники) является неуказанным (ISO C++ 10/3). Рассмотрите следующее:

struct A {
  int i;
};

class B : public A {       // 'B' is not a POD
public:
  B ();

private:
  int j;
};

Это может быть размечено как:

[ int i ][ int j ]

Или как:

[ int j ][ int i ]

Поэтому использование memset непосредственно на адресе 'этого' является в значительной степени неуказанным поведением. Один из ответов выше, на первый взгляд надеется быть более безопасным:

 memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

я полагаю, однако, что строго говоря это также приводит к неуказанному поведению. Я не могу найти нормативный текст, однако в примечании в 10/5 говорится: "Подобъект базового класса может иметь расположение (3.7) отличающийся от расположения большей части производного объекта того же типа".

В результате я компилятор мог выполнить оптимизацию пространства с различными участниками:

struct A {
  char c1;
};

struct B {
  char c2;
  char c3;
  char c4;
  int i;
};

class C : public A, public B
{
public:
  C () 
  :  c1 (10);
  {
    memset(static_cast<B*>(this), 0, sizeof(B));      
  }
};

Может быть размечен как:

[ char c1 ] [ char c2, char c3, char c4, int i ]

В системе на 32 бита, из-за выравнивания и т.д. для 'B', sizeof (B), скорее всего, составит 8 байтов. Однако sizeof (C) может также быть '8' байты, если компилятор упаковывает элементы данных. Поэтому вызов к memset мог бы перезаписать значение, данное 'c1'.

3
ответ дан 27 November 2019 в 21:23
поделиться

Если у Вас уже есть конструктор, почему не просто инициализируют его там с n1=0; n2=0; - это - конечно, больше нормальный путь.

Редактирование: На самом деле, поскольку paercebal показал, ctor инициализация еще лучше.

1
ответ дан 27 November 2019 в 21:23
поделиться

Это - идеальный пример портирования идиомы C к C++ (и почему это не могло бы всегда работать...)

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

Таким образом, что, если позже, некоторый хорошо имеющий в виду программист изменяет MY_STRUCT как так:


struct MY_STRUCT
{
    int n1;
    int n2;

   // Provide a default implementation...
   virtual int add() {return n1 + n2;}  
};

Путем добавления, что единственная функция, memset мог бы теперь вызвать опустошение. Существует детальное обсуждение в comp.lang.c +

4
ответ дан 27 November 2019 в 21:23
поделиться

Вы можете просто инициализировать значение-значение базы, и все ее члены будут обнулены. Это гарантировано

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct():MY_STRUCT() { }
};

Чтобы это работало, в базовом классе не должно быть конструктора, объявленного пользователем, как в вашем примере.

Никакого противного memset для этого. Это' s не гарантирует, что memset работает в вашем коде, даже если он должен работать на практике.

84
ответ дан 27 November 2019 в 21:23
поделиться

Если MY_STRUCT ваш код, и вы довольны использованием компилятора C ++, вы можете поместить туда конструктор, не оборачивая его классом:

struct MY_STRUCT
{
  int n1;
  int n2;
  MY_STRUCT(): n1(0), n2(0) {}
};

Я не уверен в эффективности, но я ненавижу трюки, когда вы не доказали, что эффективность нужна.

1
ответ дан 27 November 2019 в 21:23
поделиться

Я полагаю, что структура предоставлена ​​вам и не может быть изменена. Если вы можете изменить структуру, то очевидным решением будет добавление конструктора.

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

#include <stdio.h>

#define MY_STRUCT(x) MY_STRUCT x = {0}

struct MY_STRUCT
{
    int n1;
    int n2;
};

int main(int argc, char *argv[])
{
    MY_STRUCT(s);

    printf("n1(%d),n2(%d)\n", s.n1, s.n2);

    return 0;
}
0
ответ дан 27 November 2019 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: