понедельник, 24 августа 2009 г.

Блокирование наследования в С++

Представим на минуту что мы пишем инструментальную библиотеку, в которой есть менеджер сессий входа в систему, причем реализовать его требуется как синглтон и кроме того заблокировать возможность наследования от этого класса, поскольку пр наследовании и переопределении методов менеджера программа может работать неустойчиво или попросту падать.
Итак перед нами интересный вопрос на эрудицию: как заблокировать наследование от класса в С++? Напомню, что в C# и Java такая возможность существует. Как же ее реализовать в С++?
Многие скорее всего кинуться делать макросы или просто попытаются пихнуть в базовый класс какую-нибудь статическую переменную для проверки пронаследован класс или нет. Однако это не поможет. При та5ком подходе мы упускаем одну очень важную деталь: все эти проверки будут происходить во времяы выполнения и кроме того всегда возвращать неверные значения, поскольку по правилам C++ конструктор базового класса ВСЕГДА вызывается первым.
Так как же быть?
Ответ настолько же прост насколько и сложен: необходимо перенести деструктор класса в private часть класса. Читатель может возразить: при чем же здесь деструктор?

Предлагаю взглянуть на следующий фрагмент кода:


class SessionManager
{
public:
bool login(std::string& name, std::string& password);
void logout();
static bool create();
static bool destroy();
static SessionManager* instance();
private:
static SessionManager* m_self;
// блокируем констрктор
SessionManager();
// блокируем деструктор!!!
~SessionManager();
// блокируем оператор присваивания
SessionManager& operator = (const SessionManager&);
};

class SessionManagerDerived : public SessionManager
{
............
............
static bool create() {
// ошибка времени компиляции:
// невозможно создать объект класса с закрытым деструктором!
if (!m_self) return new SessionManagerDerived();
return m_self;
}
};



Вот и вся фишка: компилятор не позволит создать класс с закрытым деструктором, поскольку не знает как его удалять! Замечу что для класса SessionManager компилятор имеет эти данные хоть они и в private части.

вторник, 24 февраля 2009 г.

Всем доброго времени суток!

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

Проект
Текстовые файлы
Файл1.txt
Файл2.txt
Файл3.doc
Рисунки
Рис1.jpg
Рис2.bmp
Рис3.png
......
......

Логично что во время выполнения программы должен существовать один единственный проект. В большинстве случаев в такой ситуации используют шаблон Singletone. Да,это не плохое решение, но я пошел немного дальше. Известно, что при написании любой программы на Qt экземпляров класса QApplication также не может быть более одного. Но вернемся к проекту, описать эту абстракцию можно следующим образом:

class QProject // класс проекта
{
public:
Q_DISABLE_COPY(QProject) // блокируем копирование объекта

// добавить файл в проект
void appendFile(const QString& filename)
{
if (!m_filesList.contains(filename))
m_filesList.append(filename)
}
// удалить файл из проекта
void removeFile(const QString& filename)
{
int index = m_filesList.indexOf(filename));
if (index != -1)
m_filesList.remove(index);
}
void load(constQString& filename)
{
........
........
}

void save(constQString& filename)
{
........
........
}
private:
QProject(){} // блокируем конструктор
QStringList m_filesList;
};

Уже любопытно не правда ли? Экземпляр класса QProject невозможно создать никаким образом! Далее пронаследуемся от QApplication:

class QMyApplication : public QApplication
{
public:
QMyApplication(int argc, char *argv[]) :
QApplication(argc, argv)
{}
void createProject();
void closeProject();
QProject *project() const;
private:
QProject m_project; // указатель на проект
};

Пока вроде ничего необычного. А теперь собственно трюк:

class QMyApplication;

class QProject // класс проекта
{
public:
Q_DISABLE_COPY(QProject) // блокируем копирование объекта

// добавить файл в проект
void appendFile(const QString& filename)
{
if (!m_filesList.contains(filename))
m_filesList.append(filename)
}
// удалить файл из проекта
void removeFile(const QString& filename)
{
int index = m_filesList.indexOf(filename));
if (index != -1)
m_filesList.remove(index);
}

private:
QProject(){} // блокируем конструктор
QStringList m_filesList;
friend class QMyApplication;
};

class QMyApplication : public QApplication
{
public:
QMyApplication(int argc, char *argv[]) :
QApplication(argc, argv),
m_project(NULL),
refCount(0)
{
}
void createProject()
{
if (refCount < 1)
{
m_project = new QProject();
}
}
void closeProject()
{
if (refCount > 0)
{
delete m_project;
m_project = NULL;
}
}
QProject *project() const { return m_project; }
private:
int refCount; // счетчик ссылок
QProject m_project; // указатель на проект
};


Что получается: QProject не имеет возможности создать себя сам, однако экземпляр можно создать только через QMyApplication, который по умолчанию Singltone! Таким образом можно гарантировать, что во время работы программы будет создан лишь один единственный экземпляр QProject! Более того, переписав макрос qApp можно получать доступ к проекту из любой точки программы!

среда, 28 января 2009 г.

Разработка кроссплатформенных приложений с использованием библиотеки Qt

Практика моя не стоит на месте, поэтому осваивая все новые и новые высоты мастрества программирования, я решил, что для многих читателей будет полезен цикл сообщений по использованию библиотеки Qt. Тот кто уже пользуется возможно найдет нечто новое для себя, тот кто еще не пользуется надеюсь после прочтения захочет хотя бы посетить сайт компании (http://www.qtsoftware.com/)
Подробно описывать библиотеку не имеет никакого смысла, по этому поводу написано впечатляющее количество книг, и самое главное есть полная документация к коду, не говоря уже о том, что библиотека распространяется бесплатно в исходных кодах по лицензии GNU GPL.
Итак, отрывая этот цикл статей, кратко опишу все достоинства и недостатки библиотеки.
Достоинства: практически полная платформонезависимость кода. Для не поддерживаемых данной библиотекой платформ сейчас софта пишется очень мало (о многих поддерживаемых компиляторах и платформах я вообще узнал из списка поддерживаемых компиляторов и платформ на все том же сайте Qt Software).
Сама организация архитектуры классов сделана крайне грамотно, с использованием новейших техник программирования. Мощная мета-объектная система (по функционалу практически ничем не уступает C#, а если учесть что все это воистину кроссплатформенно, то и качественно превосходит C#). Как в C# есть сборщик мусора .
Замечательный модуль QtGui позволяющий тонко и гибко работать с любыми элементами управления начиная от пресловутых кнопочек и кончая таблицами баз данных workspace'ами и прочими. К слову в графическом интерфейсе всезде используется крайне удачная модель MVC (модель-вид-контроллер) значительно упрощающая коммуникацию приложения с пользователем.
Огромный функционал для написания графических движков, в том числе с использованием OpenGL графики (все аффинные преобразования любых фигур, в том числе картинок, проверка столкновений, ну практически готовый игровой 2D движок!).
Сушествует даже Qt вариант STL, QList, QVector, QHash, QMap и прочие, поддержка итераторов в стиле STL и Java (кому что больше нравится).
Поддержка многих драйверов баз данных, архиваторов (zip, tar...), форматов картинок (jpg, png, gif, svg, ... и многие многие другие).
В общем этот список можно продолжать еще очень долго. Сразу видно, что Qt по функционалу разом бьет и C# и gdi+.
Недостатки: да, достоинств конечно много, но есть и не достатки (куда уж без них!). Существуют проблемы с развертыванием приложения, особенно с плагинами (базы данных, некоторые форматы картинок и архиваторов), не приятно что под Windows приходится вместе с приложением таскать Qt'шные библиотеки, а если еще вы и обновить вздумаете библиотеку, то приложение вообще может не запуститься.
Время от времени обнаруживаются проблемы со строками.
Библиотека классов с геометрическими фигурами написана конечно здорово - много всего полезного, но вот получить все точки фигуры из абстрактного класса стоящего на вершине иерархии - не просто, а это уже промах в архитектуре, не такой большой конечно, но в некоторых случаях приходится писать заново под себя, а это, согласитесь, неприятно.
Есть некоторые неожиданности и в графическом интерфейсе.

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

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