DevDoc home page DevDoc background gradient
 
   
Имя Пароль Запомнить Зарегистрироваться

Автор: Кудинов Александр
Последняя модификация: 2007-03-26 22:21:36

Инициализация с помощью шаблонов

Введение

В практике программирования достаточно часто возникает задача, когда надо создать структуру и обнулить ее с помощью функции memset. Многие структуры Microsoft использует член структур cbSize в который надо записывать размер структуры.

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

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

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

Все примеры проверялись с помощью MS Visual Studio 2003 .NET.

Инициализация простых типов

Очень часто приходится писать классы, которые имеют несколько конструкторов. Для каждого из них приходится писать список инициализации всех членов. А что делать, когда большинство переменных класса должны иметь одни и те же значения, не зависимо от конструктора, который используется? Решение в лоб – использовать Copy/Past для списка инициализации. Это плохой способ. Во-первых, использование такого стиля написания программ – плохой тон. Надо всегда стараться использовать уже существующий код, вместо копирования. Этим мы и займемся. Во-вторых, при добавлении нового члена можно просто забыть прописать инициализацию во все конструкторы.

template <typename T, T tVal>
struct InitVar
{
	InitVar() : m_var(tVal) {}
	InitVar(const InitVar& rsrc) {m_var = rsrc.m_t;}
 
	operator T&() {return m_var;}
	operator T&() const {return m_var;}
	T* operator&() {return &m_var;}
	const T* operator&() const {return &m_var;}
	T& operator=(const T& t) {return (m_var = t);}
 
private:
	T m_var;
};

Для того чтобы объявить переменную с типом int, надо написать:

InitVar<int, 10> iTest;		//эквивалентно int iTest = 10;

Не имеет смысла объявлять так обычные переменные. Зато, если мы объявляем с помощью этого шаблона член класса – мы получаем выгоду.

class KWindow : public CWnd
{
	...
InitVar<int, 10> m_iTest;		//эквивалентно int iTest = 10;
	...
};

Вызов конструктора m_iTest и инициализация будет сделана перед выполнением тела конструктора KWindow. Это нам и требовалось! Теперь мы можем делать несколько конструкторов, и не заботится о том, чтобы выполнять одинаковую инициализацию в каждом из них.

Инициализация структур

Рассмотрим задачу первоначального обнуления структуры и как частный случай – запись в cbSize ее размера.

template <typename T>
struct SetZero : public T
{
	SetZero()
	{
		ZeroMemory(this, sizeof(T));
	}
};
 
template <typename T>
struct SetSize : public T
{
	SetSize()
	{
		this->cbSize = sizeof(T);
	}
};
 
template <typename T>
struct SetSizeClear : public T
{
	SetSizeClear()
	{
		ZeroMemory(this, sizeof(T));
		this->cbSize = sizeof(T);
	}
};

Эти три шаблона используются для обнуления, установки размера структуры и выполнения этих двух операций одновременно. Используются они следующим образом:

SetSizeClear<NOTIFYICONDATA> notify_icon;
notify_icon.hWnd = ...
notify_icon.hIcon = ....
...
Shell_NotifyIcon(NIM_ADD, &notify_icon);

Первая строчка заполняет структуру нулями и устанавливает член NOTIFYICONDATA::cbSize.

Остальные шаблоны используются аналогично.

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

class KWindow : public CWnd
{
	...
	SetSizeClear<NOTIFYICONDATA> notify_icon;
	...
};

Инициализация будет выполнена до выполнения тела конструктора.

Заключение

Используя приведенные техники можно эффективно делать инициализацию как простых, так и сложных типов. Развивая эту концепцию, можно даже выполнять инициализацию сложных типов по заданному образцу.

Обратите внимание, на случай, когда вы выполняете инициализацию POD классов/структур. Формально, после применения шаблона – результирующий тип не является POD. Это надо учитывать при написании надежного кода.

В некоторых случаях с результирующим типом можно обращаться как с POD объектом. Имейте ввиду что возможность таких действий зависит от конкретной реализации вашего компилятора. И вы должны очень хорошо понимать что и почему вы делаете.

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

Шаблоны из данной статьи можно усовершенствовать. Например, можно сделать универсальный шаблон для структур, который будет автоматически определять наличие cbSize. Этот же подход можно распространить и на другие признаки.

Подпишитесь, чтобы получать все новые статьи с сайта первым!

Оценить статью Текущий рейтинг: 3.4348. Проголосовало 23 человек.

Коментарии к статье

Нет комментариев

Добавить комментарий

Вы можете добавлять свои комментарии, пожелания или вопросы к статье. Они будут видны всем пользователям, а также автору статьи. Войдите в систему под своим именем, чтобы другие пользователи могли видеть, кто автор сообщения. Форма для входа расположена вверху страницы. Если у Вас нет аккаунта на сайте - рекомендуем зарегистрироваться. Это займет всего пару минут.

Отправлять сообщения могут только зарегистрированные пользователи

Copyright (C) Kudinov Alexander, 2006-2012

Перепечатка материалов с данного сайта запрещена без письменного разрешения автора. При перепечатке обязательно указывать ссылку на оригинал.

Generation time: 0,374125003815 seconds