Обнуление структуры в конструкторе - PullRequest
3 голосов
/ 22 апреля 2009

Широкий спектр структур используется в программировании Win32. Много раз используются только некоторые из их полей, а все остальные поля обнуляются. Например:

STARTUPINFO startupInfo; // has more than 10 member variables
ZeroMemory( &startupInfo, sizeof( startupInfo ) ); //zero out
startupInfo.cb = sizeof( startupInfo ); //setting size is required according to MSDN
startupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
//Now call CreateProcess() passing the startupInfo into it

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

class CStartupInfo : public STARTUPINFO {
public:
   CStartupInfo()
   {
       ZeroMemory( this, sizeof( STARTUPINFO ) );
       cb = sizeof( STARTUPINFO );
       dwFlags = STARTF_FORCEOFFFEEDBACK;
   }
};

Меня особенно беспокоит вызов ZeroMemory () - похоже, я полностью управляю кодом, и у класса нет vtable, и вызов ZeroMemory () безопасен, и между этими двумя фрагментами кода нет большой разницы, за исключением последний обеспечивает абстракцию. Есть какие-нибудь предостережения?

Ответы [ 8 ]

5 голосов
/ 22 апреля 2009

Для конструкций вы можете сделать:

STARTUPINFO startup_info = { sizeof(STARTUPINFO), 0 };
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK;

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

STARTUPINFO startup_info = { 0 };
startup_info.cb = sizeof(STARTUPINFO);
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK;

Если вы хотите обернуть структуры классом, я бы рекомендовал сначала попробовать ATL / WTL, поскольку обертываемые вами структуры могут уже существовать в виде классов.

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

4 голосов
/ 22 апреля 2009

Вместо создания подклассов, почему бы не создать функцию вместо этого?

STARTUPINFO CreateStartupInfo( DWORD flags ) {
  STARTUPINFO info;
  ZeroMemory(&info, sizeof(info));
  info.cb = sizeof(STARTUPINFO);
  info.dwFlags = flags;
  return info;
}

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

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

3 голосов
/ 22 апреля 2009

Я использовал предложение Тоня, но так как он часто убивает intellisense, я предпочел это:

template <typename T>
T& ZeroInit(T & data) 
{ 
  ZeroMemory(&data, sizeof(data)); 
  return data;
}

template <typename T>
T& ZeroInitCB(T & data) 
{ 
  ZeroMemory(&data, sizeof(data)); 
  data.cb = sizeof(data); 
  return data;
}

По сравнению с selfzero <> В обычном случае это еще одна строка:

STARTUPINFO si;
ZeroInitCB(si);

Но - как уже было сказано - я решил помочь intellisense;)

Возвращаемый T & иногда позволяет создавать цепочки, но я не так часто его использую.

2 голосов
/ 22 апреля 2009

Вы можете использовать шаблон:

template <class T>
class selfzero : public T
{
public:
    selfzero() {
        ZeroMemory( this, sizeof( selfzero<T> ));
    };
};

и затем:

{
    selfzero<STARTUPINFO> si;
}

Предостережение: используйте это для класса или структуры, которые имеют vtable или получили vtable позже, и это пойдет не так.

1 голос
/ 22 апреля 2009

Чтобы улучшить решение TonJ:

template <class T>
class selfzero : public T
{
public:
    selfzero() {
        ZeroMemory( (T*) this, sizeof( T ));
    };
};

Нули Т, а не собственные данные. Даже безопасно в случаях множественного наследования, vtables и подобных. Структура T должна быть размещена в памяти непрерывно, и ((T *) this) правильно настроена для других базовых классов и vtables.

1 голос
/ 22 апреля 2009

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

Я не вижу, чтобы он использовался в опубликованных материалах очень часто - единственную, которую я смог найти в быстром Google прямо сейчас, - это статья Пола ДиЛаскья в MSJ, август 1997 г.

CRebarInfo и CRebarBandInfo являются дружественными для программиста версиями C ++ структур C * REBARINFO и REBARBANDINFO, с конструкторами, которые инициализируют объекты всеми нулями перед соответствующей установкой члена cbSize

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

0 голосов
/ 23 апреля 2009

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

0 голосов
/ 22 апреля 2009

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

class zbool {
private:
  bool value;
public:
  zbool(const bool value) { ... }
  operator bool() { ... }
  // ... code skipped
};

А потом используйте такие типы:

struct MyStruct {
  zbool deleted;
};

Конечно, это не сработает, если вы попытаетесь инициализировать внешние структуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...