Программирование на C++

10 способов прострелить себе ногу

Создание веб приложений

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

Подпись для третьего не придумал

Американец изнасиловал лошадь, потому что думал, что у них родится кентавр

Помощь обездоленным якутам на дальнем севере

Благотворительность (на правах рекламы)

воскресенье, 22 декабря 2013 г.

Решение задач с Regex.One (с объяснениями)


С первого взгляда Регулярные выражения (Regex) могут показаться очень непонятными и сложными, поэтому после теоретического материала, лучше обязательно попрактиковаться на реальных примерах и упражнениях.
Лично для меня источником таких упражнений является сайт:
Прежде чем читать мои попытки пройти там задания, вам лучше самостоятельно пройти их, задания там очень легкие, если честно :)
Решение будет скоро, примерно через неделю )

Решение:

Первое задание: Letters

  • abcdefg
  • abcde
  • abc
Видно, что 3 буквы повторяются во всех трех строках abc, поэтому можно применить тупо выражение : 
abc
но вдруг нам надо 3 буквы, которые будут стоять рядом по алфавиту, то мы используем:
[a-c]{3}
То есть в этот паттерн будут входить abc, bac, cab итд. [a-c] означает группу, а {3} - то что букв всего 3.
\w{3} - любая буква, цифра или подчеркивание 3 раза

Второе задание: Digits


  • abc123xyz
  • define “123”
  • var g = 123;
Видно, что повторяются 3 цифры 123:
123 - если явно 3 цифры
\d{3} - цифры 3 раза
\w{3} - более нестрогое, если еще и буквы и _
[1-3]{3} - {123, 321, 213 итд (аналог \d{3} }

Третье задание: the Dot

Здесь уже сложнее, так как есть паттерн который НЕ НАДО выбирать

  • match text cat.
  • match text 896.
  • match text ?=+. 
  • skip text abc1
Видно, что здесь в каждом примере повторяется символ точка и перед точкой какие-то другие символы, но нельзя забывать, что в регексах есть оператор точка!, а если мы хотим найти символ точку, то нужно использовать слэш. Слэш экранирует символ.
.{3}\.
"." - любой символ {3} раза
"\." - поиск точки
По идее можно использовать точку вот так:
\.

Четвертое задание: Matching specific characters


  • match text can
  • match text man
  • match text fan
  • skip text dan
  • skip text ran
  • skip text pan
Видно, что регекс должен содержать c,m,f первым символом и не содержать d,r,p, используем группировку:
[cmf]an

Пятое задание: Excluding specific characters

  • match text hog 
  • match text dog
  • skip text bog
Здесь мы должны исключить символ b, это используется с помощью спецсимвола ^. Отрицание работает ТОЛЬКО в КВАДРАТНЫХ СКОБКАХ.
[^b]og
Также по заданию можно было использовать предыдущую технику:
[hd]og

Шестое задание: Character ranges

  • match text Ana
  • match text Bob
  • match text Cpc
  • skip text aax
  • skip text bby
  • skip text ccz
Ну решение состоит в группировке, здесь я думаю все понятно:
[A-C][nop][a-c]

Седьмое задание: Catching some zzz's


  • match text wazzzzup 
  • match text wazzzup
  • skip text wazup
Оч хороший пример, видно, что wazzz повторяется в двух примерах, но в некоторых жизненных ситуациях z может повторяться и больше 3ех раз, поэтому лучше использовать квантификаторы, они обозначаются обычно в фигурных скобках:
waz{3}z?up
z{3} //значит, что z может повторяться только 3 раза
z? //значит, что z может повторяться от 0 до 1 раза. (можно заменить на {0,1}
waz{3}z{0,1}up
Более лаконично:
waz{2,4}up

Восьмое задание: Mr Kleene


  • match text aaaabcc
  • match text aabbbbc
  • match text aacc
Как обычно группировка:
a{2,4}b{0,}c{1,2}
Но это не лаконично:
[abc]+ //Плюс значит один или более {1,} - эквивалент
Совсем не лаконично:
a*b*c*

Девятое задание: Characters Optional


  • match text 1 file found?
  • match text 2 files found?
  • match text x files found?
Хм, мы можем использовать логический оператор или ("|") и применить к нему квантификатор от 0, потому что число может быть и двухзначным и трехзначным итд.
К тому же на конце file может быть s или не может.
И нельзя забывать, что "?" - метасимвол, его нужно экранировать.
(\d|x){0,} files? found\?

Десятое задание: All this whitespace



  • match text 1.      abc
  • match text 2.           abc
  • match text 3.                    abc
  • skip text 4.abc

Увы, здесь пробелы при копировании не отобразились но суть в том, что нужно использовать метасимвол \s который обозначает пробел
\d\.\s{1,}abc

Одиннадцатое задание: Starting and ending



  • match text Mission: successful
  • skip text Last Mission: unsuccessful
  • skip text Next Mission: successful upon capture of target


Здесь в этом задании надо использовать символы окончания и начала строки (^ - начало и $ - конец).
^M - первая строка начинается с M
^[^LN] - первая строка не начинается с L или N
\ssuccessful$ - так как перед successful пробел, successful - идет в конце
\bsuccessful$ - \b - разделитель между словами

Двенадцатое задание: Match Groups


your task text capture
capture text file_a_record_file.pdf       (file_a_record_file)
capture text file_yesterday.pdf            (file_yesterday)
skip text testfile_fake.pdf.tmp
Суть в том, что нужно чтобы результатом выборки были строки в скобке. Делать это надо через механизм запоминающих скобок: "( )"

(file.*)\.pdf$
Видно, что на конце должно быть расширение .pdf, поэтому ставим знак окончания строки. Потом в запоминающих скобках выражение, которое содержится во всех строках. Запоминающие скобки помогают возвратить результат работы регекса.

Тринадцатое задание: Nested Groups


capture text Jan 1987 (Jan 1987, 1987)
 capture text May 1969 (May 1969, 1969)
 capture text Aug 2011 (Aug 2011, 2011)

Суть в том, что мы должны использовать ВЛОЖЕННЫЕ запоминающие скобки.
Например мы хотим захватить все содержимое, мы делаем в скобках (.*) - это захватит к примеру всю строку Jan 1987, плюс мы хотим еще вывести год, тогда мы добавляем в скобках еще скобки
(.*(\d{4}))
Еще более точно:
(\w+\s(\d{4}))

Четырнадцатое задание: More Work Group

(\d+)x(\d+)

15
I love (cats|dogs)
16

пятница, 20 декабря 2013 г.

Типаж (traits) в PHP



Новая фича, которая появилась с бетой PHP 5.4, называется Трейт (Типаж).
Трейт - единица повторного использования кода.

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

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

Совет для лучшего понимания и осмысления трейтов:
Это просто копипаст, то есть как будто в классе взяли и сделал include 'some.php' с каким то методом.

Еще информацию можно найти здесь:
http://habrahabr.ru/post/130000/ 
http://www.php.net/manual/ru/language.oop5.traits.php

Правила:

  • Трейт нельзя инстанцировать в какой-то объект (сначала надо добавить его в класс). 
  • В трейт можно добавлять и методы и переменные. 
  • Классы могут сколько угодно содержать трейтов. 
  • Трейт может содержать другие трейты 
  • Если разные классы будут использоват трейт со статическим свойством, то значение этого свойства будет разное. 
  • В отличие от обычного наследования. 
  • В типаже можно объявлять статические методы, но нельзя объявлять статические свойства. Но это можно обойти. 

1. Синтаксис

Трейт похож на класс, его объявление начинается со слова trait:

trait TSingleton {
  private static $_instance = null;

  public static function getInstance() {
    if (null === self::$_instance)
    {
      self::$_instance = new self();
    }

    return self::$_instance;
  }
}
class FrontController {
  use TSingleton;
}


// Также может быть использован в уже унаследованном классе
class WebSite extends SomeClass {
  use TSingleton;
}

2. Класс может также содержать неограниченное количество трейтов

(пример взят с википедии), трейты указываются через запятую:

// Шаблон
trait TBounding {
  public $x, $y, $width, $height;
}
trait TMoveable {
  public function moveTo($x, $y) {
    // ...
  }
}
trait TResizeable {
  public function resize($newWidth, $newHeight) {
    // ...
  }
}

// Несколько трейтов
class Rectangle {
  use TBounding, TMoveable, TResizeable; 
 
  function fillColor($color) {
    // ...
  }
}

3. Трейт может содержать другие трейты:

trait HelloWorld
{
    use Hello, World;
}
class MyWorld
{
    use HelloWorld;
}

4. Трейты и порядок выполнения.

Не стоит забывать, что:

1)Сначала:
Методы трейта переписывают унаследованные методы родительского класса
2)Потом уже:
Методы определенные в текущем классе переписывают методы из трейта

trait Hello
{
    function sayHello() {
        return "Hello";
    }
    function sayWorld() {
        return "Trait World";
    }
    function sayHelloWorld() {
        echo $this->sayHello() . " " . $this->sayWorld();
    }
    function sayBaseWorld() {
        echo $this->sayHello() . " " . parent::sayWorld();
    }
}
class Base
{
    function sayWorld(){
        return "Base World";
    }
}
class HelloWorld extends Base
{
    use Hello;
    function sayWorld() {
        return "World";
    }
}
$h =  new HelloWorld();
$h->sayHelloWorld(); // Hello World
$h->sayBaseWorld(); // Hello Base World

Объяснение:
Мы имеем результат Hello World, так как у нас в текущем классе (производном) определен метод, который ПЕРЕКРЫВАЕТ метод из трейта.
Поэтому,если мы хотим, чтобы HelloWorld объект выводил также Base World, нам необходимо использовать ключевое слово ::parent.

5. Конфликт имен

Может возникнуть ситуация, что два трейта содержат функцию с одинаковым именем, тогда PHP выкинет fatal error ошибку

trait Game
{
    function play() {
        echo "Playing a game";
    }
}
trait Music
{
    function play() {
        echo "Playing music";
    }
}
class Player
{
    use Game, Music;
}
$player = new Player();
$player->play();

Решение:
Нужно использовать ключевое слово instead:

class Player
{
  use Game, Music {
       Music::play insteadof Game;
  }
}
$player = new Player();
$player->play(); //Выведет: Playing music

Категория отличие от обычного наследования: 

6. Статические свойства в трейтах

Как мы помним, в наследовании, если наследуется класс со статическим свойством, то это статическое поле содержит одно и то же значение, в трейтах не так, класс в трейтах использует разные объекты;

Пример на родительских классах:

class TestClass {     
    public static $_bar; 
} 
class Foo1 extends TestClass { } 
class Foo2 extends TestClass { }
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World'; 
echo Foo1::$_bar . ' ' . Foo2::$_bar; // Выведет: World World
Пример с использованием трейта:

trait TestTrait {     
    public static $_bar; 
} 
class Foo1 {     
    use TestTrait; 
} 
class Foo2 {    
    use TestTrait; 
}
Foo1::$_bar = 'Hello';
Foo2::$_bar = 'World'; 
echo Foo1::$_bar . ' ' . Foo2::$_bar; // Выведете: Hello World

7. Доступ из трейта к свойствам наследуемого класса:

Трейт иммет доступ к свойствам класса, который унаследовал трейт, в отличие от обычного наследования:

trait MyTrait{   
    protected function accessVar()   {     
       return $this->var;   
    } 
} 
class TraitUser{   
    use MyTrait;   
    private $var = 'var';   
    public function getVar()   {     
         return $this->accessVar();   
    } 
} 

$t = new TraitUser(); 
echo $t->getVar(); // -> 'var'  

четверг, 19 декабря 2013 г.

Разница между $this и self в PHP. Подробная версия.


У каждого обычно возникает вопрос, что такое $this, что такое self, для чего они используются и в чем разница между ними?
ПРАВИЛА КОТОРЫЕ ВЫ ДОЛЖНЫ ЗАПОМНИТЬ:

  1. Статические функции должны использовать только статические переменные.
  2. self ТОЛЬКО для статических функций, свойств. Но также можно вызвать нестатический метод, КАК СТАТИЧЕСКИЙ через self. Но лучше так не делать, а то папа побьет.
  3. this ТОЛЬКО для нестатических.
  4. this требует, чтобы класс был проинстанцирован, self не требует.

$this - это ССЫЛКА на ТЕКУЩИЙ объект и она нужна, чтобы обратиться к переменной в КОНТЕКСТЕ класса. К примеру:

class human {
     public $name;
     public function klassFunc() {};
     function getName() {
            echo $name; // неправильно, обратиться не к переменной public name
            echo $this->name; // правильно
            
            //вызов метода класса
            $this->klassFunc();
     }
}

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

class human {
     public $name;   
     public function __construct($name){
          $this->name = $name;
          $name = $name; //будет ошибка
     }
}

$self используется в том, же самом ключе, но уже для СТАТИЧЕСКИХ свойств:

class human {
     static $name = "ololo";
     
     public function klassFunc() {};
     function getName() {
            echo self::$name; //обращение к name
            echo $this->name; //null или ошибка. НЕЛЬЗЯ
     }
}

Проверочная программа которая показывает главную разницу между self и $this:

class Klass {
const       
STAT 'S' // константы всегда статичныstatic     $stat 'Static' ;
public     
$publ 'Public' ;
private    
$priv 'Private' ;
protected  
$prot 'Protected' ;

function 
__construct( ){  }

public function 
show( ){
    print 
'<br> self::STAT: '.self::STAT// ссылается на константу
    
print '<br> self::$stat: '.self::$stat// статическая переменная.
    
print '<br>$this->stat: '.$this->stat // Ошибки нет, но выведет пустую строку. ПРЕДУПРЕЖДЕНИЕ.
    
print '<br>$this->publ: '.$this->publ // выведет как надо
    
print '<br>' ;
}
}
$me = new Klass() ;$me->show() ;

Результат:
self::STAT: S
self::$stat: Static
Strict Standards: Accessing static property Klass::$stat as non static in htdocs\test\testReference.php on line ..
Notice: Undefined property: Klass::$stat in C:\xampp\htdocs\test\testReference.php on line 50
$this->stat:
$this->publ: Public
Видно, что php умный и кидает предупреждения глупым программистам. Не делайте так.

Второе главное отличие состоит в том, что

self не использует таблицу виртуальных методов, то есть:
(взято с StackOverflow)

class Person {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();
Выведет:

Hello, I'm Ludwig the geek
Goodbye from Ludwig the person

Пояснение:
Метод sayHello использует this, поэтому виртуальная таблица вызовет Geek::getTitle()
Метод sayGoodbye использует self, поэтому виртуальная таблица будет не задействована и будет вызван parent::getTitle()

Решение:
Используем вместо self, static keyword:
public function sayGoodbye() {
      echo "Goodbye from ".static::getTitle();

Всем, спасибо, за внимание :)

среда, 14 августа 2013 г.

Интеграция библиотеки GObject/Glib в Code::Blocks


GObject - библиотека, которая является надстройкой над С, добавляющая в этот язык всю мощь Объектно ориентированного программирования.
Выглядит, конечно, как велосипед, везде макросы, очень странные требования, создание нескольких структур под разные цели (Private структура, Public структура итд), к тому же я не понимаю в чем смысл использовать ее, когда есть великий и могучий С++, но, если выбора нету, то придется использовать.
Причем GObject очень часто используется в GNOME. Напомню, что GObject является частью GTK+ (GIMP Toolkit). Для справки: На основе GTK+ написано окружение GNOME, XFCE, LXDE, KDE.
Этапы интеграции:
1) Установка Code::Blocks
2) Установка GObject (zypper install gcc gcc-c++ glib2-devel)
3) Создаем любой проект, где выбираем язык С.
4) Найдем где используется наша GObject библиотека:
     pkg-config --cflags --libs gobject-2.0
Получили результат (мой случай, в вашем может быть другой путь):
  -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -lgobject-2.0 -lglib-2.0
5) Идем в Project->Build Options->Compiler Settings->Other Options
6) Копируем (-I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include) в текстовое поле
7) Идем в Project->Build Options->Linker Settings->Other Linker Options
8) Копируем (-lgobject-2.0 -lglib-2.0) в текстовое поле
9) Строим проект
10) :EEE Все работает
Если есть вопросы, пишите, спасибо за внимание :3

суббота, 15 июня 2013 г.

Конвертер Hex String в Brush (Color) в Windows 8 (C#)

В приложениях Windows 8 для хранения цветов используется модель - RGBA (Red, Green, Blue, Alpha). Эта модель (цветовое пространство) известна всем со школы. 
В C# байты сначала идут - Alpha - Red - Green - Blue. Для каждого значения по 2 байта. 
Пример #FF000000 - Черный цвет, #FFFF0000 - Красный цвет.
Для тех, кто не знает, что такое Alpha - оно отвечает за прозрачность картинки, то есть, если у вас например RGBA = #FF000000 , здесь Alpha = 255, то есть картинка полностью непрозрачна, если Alpha = 0, то картинка полностью прозрачна.
Итак мне понадобилось сделать для моего приложения Windows Store простенький конвертер из Hex Строкового значения в объект типа SolidColorBrush. Преобразует строки типа - #FFFFFFFF. Проверок на длину строки нету.

public static class HexToColorConverter
    {
        public static SolidColorBrush GetColorFromHex(string hexColor)
        {
            hexColor = hexColor.Replace("#", ""); //удаляем решетку
            byte a = byte.Parse(hexColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); //получаем alpha
            byte r = byte.Parse(hexColor.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); //получаем красный
            byte g = byte.Parse(hexColor.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); //получаем зеленый
            byte b = byte.Parse(hexColor.Substring(6, 2), System.Globalization.NumberStyles.HexNumber); //получаем синий
            return new SolidColorBrush(Color.FromArgb(a, r, g, b));
        }
    }

пятница, 14 июня 2013 г.

Хранение пароля и логина в приложении Windows 8 (C#)


Для хранения логинов и паролей в Windows 8 приложениях, Microsoft представила новый API, называемый PasswordVault (Хранилище паролей). При добавлении в это хранилище, происходит автоматическое шифрование данных, что очень удобно, если вы не хотите изобретать велосипед. Это только основы, для всех методов ссылка на MSDN
Для удобного взаимодействия с PasswordVault, я сделал 3 простых метода:
  • Метод добавления данных (SaveCredential)
  • Получения данных (GetCredential)
  • Удаления данных (RemoveCredential)
Особенности:

  • Помним, что для использования PasswordVault, требуется подключение библиотеки Windows.Security.Credentials
  • Для получения пароля или удаления данных, нужно знать логин
  • Если не найдены данные, то выкидывается исключение, к-ое надо обработать
  • Посмотреть добавленные данные можно в Control Panel -> User Accounts .. -> Credential Manager


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

private const string RESOURCE_NAME = "MyAppCredential"; //для добавления нужно название нашего ресурса
 
private void SaveCredential(string userName, string password)
{
    var vault = new PasswordVault(); //инициализируем хранилище
    var credential = new PasswordCredential(RESOURCE_NAME, userName, password);
    //добавляем данные, с помощью метода Add
    vault.Add(credential);
}
 
private void GetCredential()
{
    string userName; 
    string password;
 
    var vault = new PasswordVault();
    try
    {
        var credential = vault.FindAllByResource(RESOURCE_NAME).FirstOrDefault();
        if (credential != null)
        {
            // Получаем пароль и логин
            userName = credential.UserName;
            //ДЛЯ ПОЛУЧЕНИЯ ПАРОЛЯ НУЖНО ЗНАТЬ ЛОГИН
            password = vault.Retrieve(RESOURCE_NAME, userName).Password;
        }
    }
    catch (Exception)
    {
        // Если ничего не найдено, то обработать ошибку
    }
}
 
private void RemoveCredential(string userName)
{
    var vault = new PasswordVault();
    try
    {
        // Удаляем данные
        //ДЛЯ УДАЛЕНИЯ ДАННЫХ НУЖНО ЗНАТЬ ЛОГИН
vault.Remove(vault.Retrieve(RESOURCE_NAME, userName)); } catch (Exception) { // Если ничего не найдено, то обработать ошибку } }

воскресенье, 10 марта 2013 г.

Шаблон проектирования Команда (Command) + Реализация на C#

Существуют ситуации, когда у нас есть просто данные, над которыми нужно провести одну лишь единственную операцию, например, у нас есть кино на DVD диске и нам нужно всего лишь либо, запустить фильм, либо остановить, либо нажать на паузу и так далее. Также нам может понадобится такое действие, которое может отменить предыдущее (например операция undo в блокноте). Я могу написать функции (или методы), которые будут исполнять нашу команду и обрабатывать ее, но у нас будет очень много похожего кода, так как у нас не будет базового класса, который будет иметь поля схожие для всех команд. К тому же код будет очень не гибким. Фактически это КЛАСС, который инкапсулирует один единственный метод execute(). Это позволяет нам хранить команды в каких то контейнерах, обрабатывать их с помощью наших обработчиков.
(Пример стыбзен с википедии). Мы проектируем калькулятор со стандартными операциями сложения, деления, умножения, вычитания
Все строится сначала на классе Command:

abstract class Command
  {
    public abstract void Execute(); //метод выполнения команды
    public abstract void UnExecute(); //метод отмены
  }
Теперь делаем производные классы от команды. Причем этот класс должен хранить получателя(Calculator), класс, который принимает команды и обрабатывает их.

class CalculatorCommand : Command
  {
 char @operator; //оператор
 int operand;
 Calculator calculator; //ОБЪЕКТ ПОЛУЧАЮЩИЙ КОМАНДЫ (Receiver)
 
    // Constructor
 public CalculatorCommand(Calculator calculator,
    char @operator, int operand)
    {
      this.calculator = calculator;
      this.@operator = @operator;
      this.operand = operand;
    }
 
    public char Operator
    {
      set{ @operator = value; }
    }
 
    public int Operand
    {
      set{ operand = value; }
    }
 //здесь мы просто вызываем метод нашего получателя, на вход которого мы отправляем оператор и операнд
    public override void Execute() 
    {
      calculator.Operation(@operator, operand);
    }
 
    public override void UnExecute()
    {
      calculator.Operation(Undo(@operator), operand);
    }
 
    // Private helper function : приватные вспомогательные функции
    private char Undo(char @operator)
    {
      char undo;
      switch(@operator)
      {
        case '+': undo = '-'; break;
        case '-': undo = '+'; break;
        case '*': undo = '/'; break;
        case '/': undo = '*'; break;
        default : undo = ' '; break;
      }
      return undo;
    }
  }
Теперь нужен класс который будет получать команды и их обрабатывать.

// "Receiver" : получатель
 
  class Calculator
  {
    private int curr = 0;
 
    public void Operation(char @operator, int operand)
    {
      switch(@operator)
      {
        case '+': curr += operand; break;
        case '-': curr -= operand; break;
        case '*': curr *= operand; break;
        case '/': curr /= operand; break;
      }
      Console.WriteLine(
        "Current value = {0,3} (following {1} {2})",
        curr, @operator, operand);
    }
  }
Теперь нужен класс, который будет отдавать команды на обработку калькулятору..

class User
  {
    // Initializers
    private Calculator _calculator = new Calculator();
    private List _commands = new List();
 
    private int _current = 0;
 
    public void Redo(int levels)
    {
      Console.WriteLine("\n---- Redo {0} levels ", levels);
 
      // Делаем возврат операций
      for (int i = 0; i < levels; i++)
        if (_current < _commands.Count - 1)
          _commands[_current++].Execute();
    }
 
    public void Undo(int levels)
    {
      Console.WriteLine("\n---- Undo {0} levels ", levels);
 
      // Делаем отмену операций
      for (int i = 0; i < levels; i++)
        if (_current > 0)
          _commands[--_current].UnExecute();
    }
 
    public void Compute(char @operator, int operand)
    {
 
      // Создаем команду операции и выполняем её
      Command command = new CalculatorCommand(
        _calculator, @operator, operand);
      command.Execute();
 
      // Добавляем операцию к списку отмены
      _commands.Add(command);
      _current++;
    }
  }
Ну и пример работы с нашим калькулятором

class MainApp
  {
    static void Main()
    {
      // Создаем пользователя.
      User user = new User();
 
      // Пусть он что-нибудь сделает.
      user.Compute('+', 100);
      user.Compute('-', 50);
      user.Compute('*', 10);
      user.Compute('/', 2);
 
      // Отменяем 4 команды
      user.Undo(4);
 
      // Вернём 3 отменённые команды.
      user.Redo(3);
 
      // Ждем ввода пользователя и завершаемся.
      Console.Read();
    }
  }
Итак, теперь обобщим все, что мы здесь написали, вот схемка шаблона:
Ну, кто не силен в английском, могу объяснить, сначала наш вызывающий (по английски Invoker) (в нашем случае User), вызывает команду (причем команда передается в виде объекта) и посылает ее получателю (Calculator), где команда успешно обрабатывается калькулятором.
Благодаря такому шаблону, мы можем без труда можем добавить свои команды, которые с помощью метода execute() будут также выполнятся. Всего лишь надо унаследовать все от Command.

пятница, 1 марта 2013 г.

Сигналы и слоты в Qt

Сигналы и слоты в Qt - это механизм, который используются для взаимодействия между несколькими объектами. Фактически, сигналы и слоты помогают нам реализовать шаблон "Наблюдатель" (скоро описание этого шаблона будет добавлено в блог, если в скором времени не появится, пинайте меня, в C# это интерфейс System.IObserver<T>, в бусте boost.signals). Эта особенность существенно различает Qt от других библиотек.

Основная идея слотов и сигналов:
Когда событие происходит, то вырабатывается сигнал, который посылается на слот, слот это обработчик сигналов, фактически, это обычный метод класса (их даже можно делать виртуальными), который определяется после идентификатора public slots.
Предположим, что у нас есть класс MyClass:
(Не забываем, что для использования этого механизма нужен специальный макрос Q_OBJECT, который должен быть определен в теле класса, к тому же только классы наследуемые от QObject могут содержать слоты и сигналы, поэтому сделаем наследование
/////////////////////////////////////////////////////
class MyClass : public QObject {
    Q_OBJECT
//Определение
}
////////////////////////////////
Начнем с сигналов:
Как было сказано это тоже обычные методы:
////////////////////////////////////////////////////
class MyClass: public QObject {
Q_OBJECT
public:
...
signals:
   void mySignal();
};
///////////////////////////////
Для выработки сигнала используем ключевое слово emit:
/////////////////////////////////////////////////////////////////////////////////
void MyClass ::sendMySignal()
{
   emit mySignal();
}
/////////////////////////////////////////////////
Хочу сказать, что сигналы автоматически определяются и их НЕ НАДО определять, просто оставляем объявление.
Теперь слоты:
Как видим слоты - это обычные методы, у них есть определение, в отличие от сигналов, все они размещаются после ключевого слова - slots,причем мы указываем для них уровень доступа. Также можно добавлять аргументы (этот пример без аргумента)
/////////////////////////////////////////////////////////////////////////////////////////////
class MyClass: public QObject {
Q_OBJECT
 
public slots:
   void mySlot()
   {
      ...
   }
};
///////////////////////////////////////////////////////
Соединение сигнала и слота:
Для соединения сигнала и слота мы используем метод connect(sender, SIGNAL(signal), receiver, SLOT(slot))
Для нашего примера (т.е. сигнал и слот выше имена):
connect(sender, SIGNAL(MySignal()), receiver, SLOT(MySlot()));
Благодаря этому, при возникновении сигнала MySignal() автоматически вызовется метод MySlot(), который обработает сигнал MySignal().