25 февр. 2009 г.

Инструмент "В контакте"

Начал новый проект: https://launchpad.net/vkontakte-tool

Для ленивых ходить по ссылкам, это - аналог программы Vkonpic, но работающий в Linux. Позволяет загружать на стены в контакте фотки вместо графити.
Написан на Python, то есть является кроссплатформенным.

На данный момент доступны исходный код и deb-пакет для Ubuntu.
Попозже будет доступна сборка для Windows.

11 февр. 2009 г.

Использование ссылок при обработке исключений C++

Просто предупреждаю.

Не попадитесь:
class AException {
public:
const int& x;
AException(const int& _x)
: x(_x)
{
}
}

class A {
private:
int* x;

public:
A()
{
x = new int;
*x = 2;
}
~A()
{
*x = 1;
delete x;
x = NULL;
}

void throw_func()
{
throw AException(*x);
}
};

void a_runner()
{
A a;
a.throw_func();
}

int main()
{
try
{
a_runner();
}
catch(AException& e)
{
cout << "Cought exception with x = " << e.x << endl;
}
}
Мораль сей басни такова. Когда вы в обработчике исключения попытаетесь обратиться к e.x, он уже будет некорректный, так как экземпляр объекта A будет уничтожен (вызовется деструктор) ДО того, как управление передается обработчику исключения.

6 февр. 2009 г.

Нюансы множественного наследования в C++

Недавно открыл для себя виртуальное наследование в C++.

Такой способ наследования тесно связан с другой возможностью C++ - множественным наследованием. Я бы даже сказал, он вполне логично дополняет один из способов применения множественного наследования.

Рассмотрим такой пример:

class A {
public:
int var_a;
virtual void foo() = 0;
};

class B: public A {
public:
int var_b;
void bar()
{
foo();
}
};

class C: public A {
public:
int var_c;
void foo()
{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};
Все дальнейшие манипуляции с классами будем проводить в main().
Например, объявим экземпляр класса D и попробуем присвоить что-нибудь переменной var_a:
D d;
d.var_a = 2;
Компилятор не пропустит такое выражение и вот почему. Дело в том, что при использовании обычного невиртуального наследования экземпляр объекта D имеет следующую логическую структуру:

Как видно из схемы, мы имеем две переменных var_a, одна их которых принадлежит классу A, который является базовым классом для B, а другая принадлежит классу A, который является базовым классом для C.
Для обхода этой ситуации можно конечно явно указать, какую переменную мы имеем ввиду:
d.B::A::var_a = 2;
или:
d.C::A::var_a = 2;
Но это, согласитесь, не очень красиво, да и не всегда логически необходимо иметь два экземпляра базового класса.
Вот именно в этом случае к нам приходит на помощь виртуальное наследование.
Есть и еще один нюанс в нашем примере: взгляните на функцию B::bar(). В ней происходит вызов виртуальной функции A::foo(), которая для этой ветви иерархии неопределена - ни в классе A, ни в классе B, ни в классе D. К классу C функция B::bar() доступа не имеет, так как C по сути наследует от другого экземпляра A.

Что же будет, если применить виртуальное наследование? Слегка изменим наш пример:
class A {
public:
int var_a;

virtual void foo() = 0;
};


class B: public virtual A {
public:
int var_b;

void bar()
{
foo();
}
};


class C: public virtual A {
public:
int var_c;
void foo()

{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};

Теперь экземпляр класса D будет иметь другую логическую структуру:

Как видно из рисунка теперь для классов B и C создается единственный общий экземпляр класса A.
Таким образом код
D d;
d.var_a = 2;

становится допустимым.

При применении виртуального наследования становится возможном и еще один интересный финт ушами - использование виртуальной функции, фактически определенной не в родительском и не в дочернем классах, а в классе-брате. В нашем примере - это виртуальная функция A::foo(), которая определена в классе C - C::foo(). Посмотрите, она используется из функции B::bar(), который является братом класса C.
Естественно, чтобы это работало, мы должны создать экземпляр такого объекта, который бы наследовал от обоих классов B и C, в нашем случае - это класс D. Таким образом, становится допустимым код:
D d;
B& b = d; //создали ссылку типа B на объект типа D.
b.bar(); //вызываем функцию bar(), которая в свою очередь вызывает функцию foo(), которая фактически определена в классе C.

Программиста, который получил в свой метод ссылку типа B, вообще может ввести в заблуждение работоспособность подобного кода, ведь он не знает о том, что на самом деле эта ссылка указывает на объект типа D (он вовсе не видит объявления класса D), который наследует и от C тоже. С его точки зрения это будет казаться мистикой - ведь функция bar() вызывает виртуальную функцию foo(), которая с его точки зрения НИГДЕ не определена!

Ну и напоследок еще одна задачка, связанная с множественным наследованием:

class B
{
public:
int var_b;
};

class C
{
public:
int var_c;
};

class D: public B, public C
{
public:
int var_d;
};

using namespace std;

int main()
{
D* d = new D;

B* b = d;
C* c = d;

void* v_d = d;
void* v_b = b;
void* v_c = c;

cout << (b == d) << endl;
cout << (c == d) << endl;

cout << endl;

cout << (v_b == v_d) << endl;
cout << (v_c == v_d) << endl;

return 0;
}
Программа выведет:
1
1
1
0

Внимание вопрос: почему в последнем случае получается ноль (false)? :)