Сообщество посвящено трюкам и красивым ходам в C++.
Основателем и модератором являюсь я, mistificator. Читать далее
URL
  • ↓
  • ↑
  • ⇑
 
22:33 

Трюк 0x0010 — Дакл, Сакл и Какл, или Шаринг в Виндах

mistificator
капелюх чарiвника
Адовая конструкция, убивающая весь смысл NTFS'а на корню:

//------------------------------------------------------------
SECURITY_DESCRIPTOR _sd;
::InitializeSecurityDescriptor(&_sd, SECURITY_DESCRIPTOR_REVISION);
::SetSecurityDescriptorDacl(&_sd, true, 0, false);
//------------------------------------------------------------


Применяем дескриптор безопасности к папке или файлу.

//------------------------------------------------------------
::SetFileSecurity(<имя файла>, DACL_SECURITY_INFORMATION, &_sd);
//------------------------------------------------------------


Результат:

//------------------------------------------------------------
"Для данного объекта нет заданных разрешений.

Предупреждение: это потенциально опасно, так как любой пользователь,
имеющий доступ к данному объекту, может стать его владельцем.
Владелец объекта должен как можно быстрее назначить разрешения."
//------------------------------------------------------------

@темы: Лень как двигатель прогресса

13:21 

Трюк 0x0001 — Компактное объявление свойств класса

Куб 0
Зачастую в классе необходимо объявить так называемые "свойства" — пары функций, отвечающие лишь за считывание и запись внутренней переменной. Например, SetColor() и GetColor(), SetText() и GetText() и так далее. Когда таких функций много, их объявление превращается в скучную рутинную работу, да и интерфейс изрядно загромождается.
С помощью нехитрого макроса можно облегчить себе жизнь и сократить объем кода.
Читать дальше...

@темы: Лень как двигатель прогресса

00:20 

Трюк 0x0007 — Узнаем, сколько создано объектов

Куб 0
В программе, изобилующей мелкими объектами, иногда необходимо выяснить, сколько именно объектов создано, сотня или же миллион. А если объекты создаются и уничтожаются постоянно в течение жизненного цикла программы, то зачастую интересует вопрос, сколько раз были созданы объекты того или иного типа?
Ключевые элементы такого счетчика объектов вырисовываются сразу: некие глобальные переменные и привязка к типам объектов.
Сразу хочется сказать о подводных камнях, которые обнаруживаются в процессе организации счетчика. Дело в том, что классы как правило имеют наследование, поэтому простое инкрементирование счетчика в каждом конструкторе никуда не годится — при вызове конструктора будут инкрементированы и счетчики всех базовых классов.
В приведенной далее реализации счетчика объектов используется соглашение, что базовый класс отмечается разработчиком. Для различения счетчиков разных объектов используется шаблонный класс, который специфицируется типом. (Возможно, альтернативным более компактным решением было бы специфицирование шаблона строкой, получаемой из typeid при подстановке this.) Счетчик, установленный в каком либо классе, обязательно должен быть установлен и во всех его производных классах, если только Вы не хотите, чтобы объекты производных классов подсчитывались в одной куче с объектами базовых классов.
Читать дальше...

Thread-safe оптимизированный вариант счетчика, предложенный моим коллегой, сопряжен с использованием множественного наследования, и содержит три класса. Однако прост для понимания, и категорически рекомендован к использованию.
Читать дальше...

@темы: Счётчики объектов, Трюки с наследованием

16:32 

Трюк 0x0008 — Never too late

Куб 0
В устоявшуюся терминологию программистов C++ входят понятие раннего связывания (на стадии компиляции) и понятие позднего, отложенного связывания (динамически, во время исполнения). Далее в статье будет предложено решение для не просто позднего, а по-настоящему запоздалого связывания данных. Представьте, что Вы можете выбирать обработчик для данных прямо во время выполнения программы и, более того, ассоциировать с данными несколько обработчиков. Представьте, что при этом работает полиморфизм. Представьте, что если Вам более не нужны выдаваемые объектом данные, Вы можете "отключиться" от объекта, как клиент от сервера.
Как такое реализовать?
Читать далее...

@темы: Нетривиальный вызов, Трюки с наследованием, Трюки с шаблонами, Философия

17:05 

Трюк 0x0011 — Жди меня, и я запущусь, только очень жди

mistificator
капелюх чарiвника
Проконтролировать факт запуска потока в Qt можно с использованием семафора. Делается это следующим образом.


//------------------------------------------------------------

QSemaphore sem(1);

class Thread: public QThread
{
public:
protected:
void run()
{
msleep(5000); // заставляем главный поток подождать 5 секунд
sem.release(); // сигнализируем об окончании ожидания
}
};

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

sem.acquire();

Thread t;
t.start();

sem.acquire(); // на этой строке будет ожидание вызова sem.release() внутри Thread::run()
sem.release();

t.wait();

return 0;
}
//------------------------------------------------------------

01:26 

Трюк 0x000F — Без труда не вытащишь контрол из комцэтээла.

Куб 0
Comctl32.dll — важная деталь в механизме под названием Windows. Именно этой библиотеке мы обязаны кнопками и прочими элементами в характерном стиле XP. Common controls... Конечно, всё это уходит в прошлое, скоро XP отметит 10-летний юбилей, солидный возраст для софта.

Итак, привычный стиль XP. Если писать приложение с нуля, то стандартный функционал WinAPI не даёт программисту и такого стиля, выдавая на-гора оквадраченные кнопки с убогим системным шрифтом.

Что делать?

Читать дальше...

@темы: .DLL

22:29 

Трюк 0x000E — Qt 4.5 из комплекта Qt Creator

mistificator
капелюх чарiвника
Всё ещё мучаетесь сообщением типа "Entry point _Z5QFreePv not found in QtCore4.dll" при попытке запустить release-cборку приложения? Всё очень просто решается. В папке bin лежат QtCore4.dll и QtGui4.dll, собранные под Visual Studio 2008. И никаким боком они c вашим приложением, собранным на MinGW, работать не будут. Возьмите QtCore4.dll и QtGui4.dll (и что вам ещё нужно) из папки qt/lib , скопируйте в папку своего приложения, и будет вам счастье.
Enjoy.

@темы: .DLL

10:44 

Трюк 0x000D — Рекурсивное наследование

mistificator
капелюх чарiвника
Практическая польза от данного трюка определённо должна быть. Как придумаете, как его использовать — сообщайте.


//------------------------------------------------------------
#include
using namespace std;

class A {
public:
A() {
cout << "ctor A" << endl;
}
virtual void foo() {
cout << "foo A" << endl;
}
};

template
class B: public T {
public:
B(): T() {
cout << "ctor B<" << typeid(T).name() << ">" << endl;
}
virtual void foo() {
cout << "foo B<" << typeid(T).name() << ">" << endl;
}
};

int main() {

A * a = new B < B < B < B < B < A > > > > > ( );
a->foo();

return 0;
}
//------------------------------------------------------------

@темы: Трюки с наследованием, Трюки с шаблонами

22:56 

Трюк 0x000C — Константные вычисления и компилятор bcc32

mistificator
капелюх чарiвника
Давно имелись подозрения, что не всё гладко в реализации шаблонов в компиляторе Borland'a (bcc32, Borland C++ Compiler). Borland благополучно отошёл от разработки компиляторов, предоставив свои детища CodeGear'у. Те немного подретушировали компилятор, подправив в нём откровенные несоответствия современным стандартам С++ (добавив, в частности, поддержку неименованных union как в MSVC++), но некоторые проблемы с реализацией шаблонов всё же остались.
Одна из них заключается в том, что приведенный ниже код не компилируется компилятором bcc32.
Читать дальше...

@темы: Нетривиальный вызов, Трюки с шаблонами

00:06 

Трюк 0x000B — Функцию вызывали?

Куб 0
Жесткая типизация в языке С++ подчас создаёт непреодолимые трудности. Например, вы, программист-разработчик, и занимаетесь реализацией некоего модульного проекта. Предположим, что это звуковой редактор с плагинами. Плагины вы реализуете в виде .dll, которые динамически подключаются при загрузке редактора. И всё бы ничего, но функции плагинов настолько разнообразны и разноплановы, что описать их в некоем формализованном виде просто нереально. В самом деле, представьте: один плагин принимает пять параметров, возвращает три; другой плагин принимает десять входных аргументов, и не выдает ни одного, зато пишет файл и рисует график на экране. Как быть в таком случае?
Читать далее...

@темы: Asm & C++, Нетривиальный вызов

00:27 

Трюк 0x000A — Не быть консерватором

mistificator
капелюх чарiвника
Иногда разработчику на С++ нужно остановиться и трезво оценить выполняемую задачу, оценить глубину своих знаний, оценить возможности STL и различных других библиотек, оценить сложность реализации элементарных парадигм средствами языка C++, и, осознав предстоящие трудности, перенести свой код в C#, например, или другой подходящий современный язык. Не будьте консерваторами!

@темы: Философия

23:47 

Трюк 0x0009 — Deep Private

Куб 0
Иногда разработчику класса нужно скрыть от пользователя некоторые особенности реализации интерфейса. Речь идет о секции private, в которой обычно размещаются переменные класса и методы, предназначенные сугубо для внутреннего использования. Пользователю вовсе необязательно видеть секцию private, особенно, если реализация класса находится внутри скомпилированной библиотеки .dll, а в руках пользователя лишь заголовочный файл.
Читать далее...

@темы: .DLL, Трюки с наследованием

22:21 

Трюк 0x0006 — Раз ссылка, два ссылка...

Куб 0
В сфере программистов C++ существует устоявшийся термин "подсчет ссылок" (reference counting), обозначающий метод работы с объектами с использованием общих данных. Как только у данных появляется новый клиент, счетчик ссылок увеличивается на единицу, как только клиент завершает работу с данными, счетчик уменьшается на единицу. Когда последний клиент завершает работу с данными, данные уничтожаются. Зачем это нужно? Предположим, в Вашем проекте нужно обработать звуковые данные разными способами и затем сравнить результаты. Каждая процедура обработки считывает данные и приступает к работе. Если данные громоздки, разумно предоставить каждому клиенту ссылку на одни и те же данные, чтобы избежать их ненужного дублирования, но при этом вовсе не нужно давать понять клиентам, что они разделяют одни и те же данные. В идеальном случае клиент получает указатель на общие данные, и начинает работать с ним как с обычным указателем на объект, а в конце работы вызывает деструктор.
Ниже предлагается шаблонный класс RefCounter, реализующий механизм подсчета ссылок. Его схема функционирования схожа с std::auto_ptr. Объект RefCounter ведет себя как указатель на класс, специализировавший шаблон, и позволяет выполнять операции присваивания и вызовы методов специализирующего класса.
Читать дальше...

@темы: Счётчики объектов

05:02 

Трюк 0x0005 — Масштабируемый n-мерный массив

Куб 0
Ни для кого не секрет, что С++, являясь языком общего назначения, а не ориентированным на определенную предметную область, во многих аспектах содержит методы лишь начального, базового уровня. В частности, не нашедшей должного отражения в синтаксисе языка является тема динамически масштабируемых многомерных массивов. Идея внести в программу двумерный масштабируемый массив вызовет у программиста тревогу, трехмерный — раздражение, а при мысли о четырехмерных и прочих n-мерных массивах программист С++, скорее всего, посоветует заказчику проекта ориентироваться на другой язык программирования.
Однако, не все настолько мрачно. Хорошая новость заключается в том, что рекурсивное наследование шаблонов и перегрузка operator[] могут позволить изготовить класс, поддерживающий n-мерные массивы с возможностью их масштабирования, и всего того, что пожелает разработчик.
Читать дальше...

@темы: Трюки с шаблонами

00:14 

Трюк 0x0004 — Исключение в конструкторе не смертельно

Куб 0
У многих есть мнение, что ресурсы, выделенные в потерпевшем крах конструкторе, очень трудно освободить, так как деструктор для объекта с незавершенным конструктором не вызывается, а указатели на уже инициализированные ресурсы теряются, что приводит к утечке памяти и прочим неприятностям.
А что, если использовать не обычные указатели, а "умные" указатели? Например, стандартный std::auto_ptr? Если память для ресурсов организовать с помощью std::auto_ptr, то проблема освобождения ее снимается с плеч разработчика.
Возьмем для примера два класса, Loser и Fixxer. Первый, в полном соответствии с названием, в конструкторе выделяет ресурс (память) и при передаче неподходящего параметра вылетает с исключением. Второй, так же в соответствии со своим названием, пытается выправить ситуацию. Посмотрим, как он с этим справляется.
Читать дальше...

@темы: Исключения

16:01 

Трюк 0x0003 — К слову о синглетонах

Куб 0
Классический синглетон — класс-одиночка — представляет собой класс, объект которого может быть создан один единственный раз за весь период выполнения программы, и который существует в течение всего времени выполнения программы. Реализовать его можно разными способами, о которых можно прочитать в литературных трудах Банды Четырех и ставшего притчей во языцех Коплиена.
Однако, наиболее компактным, и не вызывающим головной боли при эксплуатации, является приведенный ниже вариант.
Читать дальше...


Другим подходом к созданию синглетона является использование шаблона.
Читать дальше...

@темы: Паттерны, Трюки с наследованием

14:00 

Трюк 0x0002 — Автоматическое выполнение операций

Куб 0
Общеизвестно, что деструктор объекта, если объект создан путем простого объявления, вызывается автоматически в конце блока. При творческом подходе автоматический вызов деструктора удается приспособить для выполнения специфических задач.
Предположим, в разрабатываемом проекте необходимо измерять время выполнения тех или иных фрагментов кода или целых функций. Удобно подготовить класс TimeMeasure, не содержащий методов, а только лишь конструктор и деструктор. В конструкторе засекается время создания объекта TimeMeasure, в деструкторе вычисляется разность времен между выполнением деструктора и конструктора и выводится, например, на экран.
Читать дальше...

@темы: Лень как двигатель прогресса, Нетривиальный вызов

17:47 

Трюк 0x0000 — Избегание наследования

Куб 0
Как поступить, если под рукой имеется абстрактный класс, функциональность которого хочется задействовать, но наследовать свой класс от него неохота?
Пусть имеется абстрактный класс окна AbstractWindow с чисто виртуальным методом Create().

//------------------------------------------------------------
class AbstractWindow {
public:
AbstractWindow() {
// код конструктора
}
virtual ~AbstractWindow() {
// код деструктора
}

virtual int Create(const char *Title)=0 {
// общий код создания окна
}
};
//------------------------------------------------------------
Предположим, что некий класс SimpleApplication, являющийся базовым классом для простых приложений, должен вызывать функцию создания окна. Таким образом, он должен включать в себя функциональность AbstractWindow. Открытое или закрытое наследование здесь нежелательно, так как логически SimpleApplication не связан с AbstractWindow — приложение не является частным случаем окна.
Злоупотребляя механизмом наследования, можно создать производный от AbstractWindow производный класс SimpleWindow, имеющий в своем методе Create() весь необходимый арсенал для создания окна. Тогда экземпляр SimpleWindow можно было бы включить в интерфейс SimpleApplication и вызвать Create() в подходящий момент. Однако что, если для класса ComplexApplication, производного от SimpleApplication, понадобится новая реализация метода создания окна? Следует ли тогда создать производный класс ComplexWindow, открывая таким образом разработчику единственный путь — создание новых подклассов по мере создания новых видов приложений? Читать дальше...

@темы: Трюки с наследованием

Записная книжка программиста C++

главная