суббота, 29 мая 2010 г.

C++ и Static

Рассмотрим употребление ключевого static в языке С++. Думаю кому-то это сообщение будет полезно.
Рассмотрим следующие обасти видимости, имеющиеся в С++:
Local scope (область видимости - блок)
Function scope (область видимости - функция)
File scope (область видимости - файл)
Class scope (область видимости - класс/структура)

Начнем с самого простого.
Допустим в блоке находится локальная статическая переменная:
while(true)
{
    static int i=0;
    i++;
}
В этом случае переменная i будет создана лишь раз, и значение ее будет храниться втчение времени выполнения программы, несмотря на выход за пределы области видимости данного блока.

Аналогично дело обстоит с функциями:
void Foo()
{
    static int i=0;
    i++;
}
В данном случае значение переменной i будет сохраняться между вызовами Foo().
В обоих приведенных примерах статическая переменная видна только в пределах блока, в котором она определена.
Нам ничто не мешает создать глобальную статическую переменную, которая будет видна повсюду в файле и только в файле, в котором она определена, и никаким extern`ом к ней из другого файла не обратиться:
// File: example.cpp

// Global static local to this file:
   static int i;
Рассмотрим классы.
Классы, как известно, могут содрежать данные и методы, данные и методы могут быть статическими. Статические члены в большинстве случаев ведут себя так же как и нестатические. Они так же подчиняются модификаторам доступа (public, private, protected). Они содержатся только в области видимости данного конкретного класса. Доступ к данным осуществляется посредством операторов '.' или '->'. Но, в отличие от нестатических данных, к статическим можно обращаться с помощью оператора '::', используя имя класса. И для этого совсем необязательно, чтобы объект данного класса был создан.

Статические данные разделяются между всеми экземплярами класса. Они действуют подобно глобальным данным для класса.
Статические данные класса находят множество применений. Допустим, подсчет количества экземпляров данного класса:
class A
{
    static int i;
    public:
    A(){i++}
}
Паттерн одиночка(singleton) так же использует статическую переменную для того, чтобы гарантировать, что класс будет создан единожды при первом обращении.

Однажды созданная статическая переменная существуе на протяжении всего времени работы программы. Все статические переменные разрушаются (вызываются их деструкторы)при возврате из main() во время вызова atexit(). Деструкторы статической переменной вызываются только если переменная была создана и инициализирована. Это означает, что статический локальный объект в области видимости функции не нуждается в разрушении, если эта функция не была вызвана. В данном примере использование локальной статической переменной в синглтоне избавляет от проблемы утечки ресурсов, поскольку объект разрушается при выходе из программы.
class Singleton
{
    private:
    Singleton(){}
    public:
    static Singleton& Instance()
    {
        static Singleton inst;
        return inst;
    }
};
Данная разновидность синглтона называется синглтон Мейерса (подробнее Александреску)

Опишем класс со статической переменной-членом:
// File: example.h
   class A
   {
   public:
      A();
      static int i;
   };
Но объявление статической переменной внутри определения класса не является ее определением. Определение переменной должно присутствовать в области видимости того пространства имен, в котором заключено определение класса данной статической переменной. Поэтому код следует дополнить следующим образом:
class A
   {
   public:
      A();
      static int i;
   };
   int A::i = 99;
Если не инициализировтаь значение статической переменной, то по умолчанию будет присвоено значение 0.
Можно проводить инициализацию с помощью выражения (в данном случае, с помощью функции, возвращающей значение)
// File: example.cpp
   int A::i = GetValue();
Допускается инициализация внутри определения класса для переменных интегрального типа: int, char, long, bool (ISO Standard, 9.4.2), а также типов из пространства std, таких как std::float_round_style и т.д.
Статические переменные-члены не могут содержаться в локальных класса, а так же они не могут быть mutable.

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

Статические методы чаще всего используются для манипуляций со статическими данными класса:
// File: example.h
   class A
   {
   public:
      A();
      static int GetValue () { return val; }
   private:
      static int val;
   };
Статические методы также могут быть доступны посредством операторов '.' и '->' по имени объекта, либо по имени класса с помощью оператора '::'.
Статические методы не могут быть виртуальными. Не может присутствовать одновременно статический и нестатический методы с одинаковыми типами параметров. Так же статические методы не могут быть помечены модификаторами const, volatile и const volatile. Локальные классы не могут содержать статических членов

Всё...
Не совсем наверно всё. Остались моменты, которые я хотел бы обсудить с кем-либо, но ... не сейчас

воскресенье, 23 мая 2010 г.

Qt и условная сборка qmake

Попытаюсь сэкономить кому-нибудь время,силы и нервы.
В одном из Qt проектов (.lib) нужно было сделать так, чтобы при сборке в режиме debug сгенерированная dll помещалась в дерикторию debug, а при сборке в режиме release - в директорию release.
Решение состояло в том, чтобы прописать в pro-файле следующее:
CONFIG += designer plugin debug
TEMPLATE = lib
TARGET = $$qtLibraryTarget($$TARGET)
.....................

debug {
DLLDESTDIR += ../MyApp/debug
message(1)

}
release {
DLLDESTDIR += ../MyApp/release
message(2)
}
Но почему-то сгенерированная dll помещалась всегда в обе папке, независимо от режима сборки. И на консоль выдавались оба сообщения - message(1) и message(2)

Дело было в том, что переменная CONFIG уже содержала в себе значения и debug, и release.
Результат выполнения message($$CONFIG) такой:
Project MESSAGE: lex yacc warn_on debug uic resources rtti_off exceptions_off stl_off incremental_off thread_off windows release ReleaseBuild Release build_pass qt warn_on release incremental flat link_prl precompile_header autogen_precompile_source copy_dir_files debug_and_release debug_and_release_target embed_manifest_dll embed_manifest_exe debug shared stl exceptions rtti mmx 3dnow sse sse2 def_files release ReleaseBuild Release build_pass designer plugin debug
Решение состоит в использовании функции CONFIG и выглядит следующим образом:
CONFIG( debug, debug|release ) {
DLLDESTDIR += ../MyApp/debug
message(1)
} else {
DLLDESTDIR += ../MyApp/release
message(2)
}
Теперь будет выполняться только единственная ветвь в зависимости от режима сборки.