2009-04-22 3 views
3

В программировании 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() вызов - похоже, я полностью контролировать код и класс не имеет виртуальные таблиц и вызов ZeroMemory() таким образом, является безопасным и нет большой разницы между этими двумя кроме того, что последний обеспечивает абстракцию. Есть ли какие-либо оговорки?

ответ

1

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

Я не вижу, что используется в опубликованных материалах очень часто - единственный, который я мог бы найти в быстрый Google прямо сейчас статья Пола DiLascia в MSJ августа 1997 (http://www.microsoft.com/MSJ/0897/C0897.aspx):

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

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

+0

Возможно, я ошибаюсь, но эти классы, похоже, являются частью MFC? Если так, то это чертовски большая структура для использования для этого простого удобства. (Это и я сам человек ATL/WTL.) – Daemin

+0

Структуры обложек DiLascia являются частью SDK Windows, если я не ошибаюсь, но этот метод можно использовать для любой структуры POD - он не привязан к MFC, ATL , или Windows. В статье DiLascia рассказывается о работе с некоторыми виджетами MFC - это не об обертывании структур в классе, который их использует. Обвязка структур - это просто техник, который он использует и упоминает в статье. –

4

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

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

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

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

+1

Хорошее решение. Зачем? Потому что компилятор полностью оптимизирует его через NRVO! * В стеке не будет создана вторая копия * структуры, при этом не будет выполнено копирование возвращаемого значения (если вышеупомянутое используется при инициализации, то есть). Функция просто и чисто имеет нулевые служебные данные. –

+1

@ Konrad, да, я забыл про NRVO: http://msdn.microsoft.com/en-us/library/ms364057.aspx – JaredPar

-1

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

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

И затем использовать такие типы:

struct MyStruct { 
    zbool deleted; 
}; 

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

+2

-1 Вы не можете получить встроенные типы на C++. –

+0

Да, я не могу. К сожалению, я не писал на C++ годами, но у вас есть идея. – stepancheg

5

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

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 первым, так как структуры вы оберточной, возможно, уже существуют в виде классы там.

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

+0

В C++ вам даже не нужно 0. – dalle

+0

Маленькая точка: вам не нужны нули. STARTUPINFO startup_info = {sizeof (STARTUPINFO)}; (остальная часть структуры инициализируется значением) –

+1

Вы уверены? Следуя философии не оплачивая расходы, если вы не хотите, чтобы я предположил, что по умолчанию неинициализированные структуры будут иметь мусор в них. Независимо, я бы поставил нуль в явном виде. – Daemin

2

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

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

, а затем:

{ 
    selfzero<STARTUPINFO> si; 
} 

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

3

Я использовал предложение Тонджа, но так как он убивает 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 & иногда позволяет цепочки, но я не использую это часто.

1

Чтобы улучшить решение Тондж в:

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

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

0

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

+0

Встроенные типы не инициализируются по умолчанию, если инициализатор явно не включен в список инициализатора конструктора-пользователя. Структуры из заголовков WinAPI не имеют пользовательских конструкторов. – sharptooth

+0

Попробуйте сами: main.cc

  #include  #include  #include  #include  #include  #include  using namespace std; class P { public: P() { cout << "P()" << endl; } }; struct T { P p; int a; int b; }; int main(int argc, char *argv[]) { T t; }  
Он выдает "P()" – piotr

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