Russian Qt Forum
Ноябрь 24, 2024, 04:43 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 ... 6   Вниз
  Печать  
Автор Тема: Как узнать о типе, до выполнения dynamic_cast?  (Прочитано 34509 раз)
Eten
Гость
« Ответ #15 : Март 07, 2011, 09:06 »

Спасибо за ссылки, на выходных разобрался с этим вопросом и все у меня работает, но осталось два вопроса. Сначала, опишу, что я сделал, а уж потом и вопросы задам.  Улыбающийся

Итак, в класс NNumeric, который ориентирован в операциях над его значениями работать как стековая переменная, добавил следующие строки кода:
Код:
    //две строки ниже позволяют передать стековой переменной значения из указательной
    NNumeric(NNumeric* Numeric) {this->_id = Numeric->ID(); this->_constvalue = Numeric->ConstValue(); this->_name = Numeric->Name(); this->_isreal = Numeric->IsReal(); this->_value = Numeric->Value();}
    void operator= (NNumeric* a) {this->setID(a->ID()); this->setConstValue(a->ConstValue()); this->setName(a->Name()); this->setIsReal(a->IsReal()); this->setValue(a->Value());}
    //строк ниже поозволяет указательной переменной передать значения из стековой
    operator NNumeric*() {NNumeric* a = new NNumeric(); a->setID(this->ID()); a->setConstValue(this->ConstValue()); a->setName(this->Name()); a->setIsReal(this->IsReal()); a->setValue(this->Value()); return a;}

Т.о. NNumeric выполнять все, что и раньше, как стековая переменная, но теперь еще и сохраняется в базовом класса, как указатель (NNumeric*) и спокойно передается стековой переменной. Т.е. хранится он в виде указателя, а работа с ним ведется через стековую переменную. Вот здесь первый вопрос. У меня хоть и не содержится внутри класса своих указательных переменных и деструктор собирается транслятором (проше говоря он не прописан), а также в виде указателей он хранится в QList<NDataTypes*> Variables; и QStack<NDataTypes*> StackValue;, то не возникнет ли у меня утечки памяти?

Теперь, то как у меня реализована та функция, о которой я в первом указывал:
Код:
void NeorgekEngine::NEngine::Writeln(qint32 ID)
{
    NDataTypes* dt;
    QString str;

    if (ID == 0)
    {
        dt = this->StackValue.pop();
    }
    else
    {
        dt = this->Variables[ID-1];
    }

    if (dynamic_cast<NNumeric*>(dt) != NULL) {
        NNumeric numeric = dynamic_cast<NNumeric*>(dt);
        str = QString(numeric);
    }
/*
    if (dynamic_cast<NString*>(dt) != NULL) {
        NString* string = dynamic_cast<NString*>(dt);
        str = string->Value();
    }

    if (dynamic_cast<NLogical*>(dt) != NULL) {
        NLogical* logical = dynamic_cast<NLogical*>(dt);
        QString string;
        if (logical->Value())
        {
            string = "true";
        }
        else
        {
            string = "false";
        }
        str = string;
    }
*/
    this->Screen.setText(this->Screen.Text()+str+"<br\>");
}

Вот здесь второй вопрос. Есть момент, когда dt имеет указатель не инициализированный класс. Т.е. был объявлен указатель, но без вызова конструктора, потом его присвоили dt. Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?
« Последнее редактирование: Март 07, 2011, 09:11 от Eten » Записан
Eten
Гость
« Ответ #16 : Март 07, 2011, 09:09 »

Полагаю стоит дать код, еще базовых классов и самого NNumeric.

Базовые классы:
Код:
#ifndef DATATYPES_H
#define DATATYPES_H

#include <QString>

    class NDataTypes
    {
    public:
        NDataTypes() {this->_id = -1; this->_name = "";}
        virtual ~NDataTypes() { }

        qint32 ID() const {return this->_id;}
        void setID(qint32 ID) {this->_id = ID;}
        QString Name() const {return this->_name;}
        void setName(QString Name) {if (Name != NULL) this->_name = Name; else this->_name = "";}

    protected:
        //если идентификатор равен -1, значит это либо временная переменная, либо константное значение
        qint32 _id;
        //константные или временные значения имен не имеют, только переменные.
        QString _name;
    };

    class NSimpleDataTypes : public NDataTypes
    {
    public:
        NSimpleDataTypes() : NDataTypes() {this->_constvalue = false;}

        bool ConstValue() const {return this->_constvalue;}
        void setConstValue(bool ConstValue) {this->_constvalue = ConstValue;}

    protected:
        //это указывает на константное значение
        bool _constvalue;
    };


#endif // DATATYPES_H

NNumeric:
Код:
include <cmath>
#include "data-types.h"

class NNumeric : public NSimpleDataTypes
{
public:
    NNumeric(double Value = 0) : NSimpleDataTypes() {this->_isreal = true; this->setValue(Value);}

    //две строки ниже позволяют передать стековой переменной значения из указательной
    NNumeric(NNumeric* Numeric) {this->_id = Numeric->ID(); this->_constvalue = Numeric->ConstValue(); this->_name = Numeric->Name(); this->_isreal = Numeric->IsReal(); this->_value = Numeric->Value();}
    void operator= (NNumeric* a) {this->setID(a->ID()); this->setConstValue(a->ConstValue()); this->setName(a->Name()); this->setIsReal(a->IsReal()); this->setValue(a->Value());}
    //строк ниже поозволяет указательной переменной передать значения из стековой
    operator NNumeric*() {NNumeric* a = new NNumeric(); a->setID(this->ID()); a->setConstValue(this->ConstValue()); a->setName(this->Name()); a->setIsReal(this->IsReal()); a->setValue(this->Value()); return a;}

    double round() const {return floor(this->Value() + 0.5);}
    double fractional() const {return this->Value() < 0.0 ? this->Value() - ceil(this->Value()) : this->Value() - floor(this->Value());}
    double integer() const {return this->Value() < 0.0 ? ceil(this->Value()) : floor(this->Value());}

    void setIsReal(bool IsReal) {this->_isreal = IsReal;}
    //Если ложно, то значение принимается и выдается (при печати числа на экране)
    // с отсечение дробной части числа и соблюдением границ типа qint32.
    bool IsReal() const {return this->_isreal;}
    void setValue(double Value) {if (!this->IsReal()) Value = this->CheckingInt32Borders(Value); else Value = this->CheckingFloatBorders(Value); this->_value = Value;}
    double Value() const {return double(this->CheckingFloatBorders(this->_value));}

    operator QString() {return QString::number(this->_value);}
    operator double() {return double(this->CheckingFloatBorders(this->Value()));}
    operator float() {return this->CheckingFloatBorders(this->Value());}
    operator qint32() {return this->CheckingInt32Borders(this->_value);}
    void operator++(int) {if (!this->IsReal()) this->_value = this->CheckingInt32Borders(this->_value+1); else this->_value = this->CheckingFloatBorders(this->_value+1);}
    void operator--(int) {if (!this->IsReal()) this->_value = this->CheckingInt32Borders(this->_value-1); else this->_value = this->CheckingFloatBorders(this->_value-1);}

private:

    long double Int32Max() const {return 2147483647;}
    long double Int32Min() const {return -2147483647;}
    long double FloatMax() const {return 2.147483647e9;}
    long double FloatMin() const {return 2.147483647e-9;}
    float CheckingFloatBorders (double Value) const
    {
        //В этой функции +/-inf приравниваются к max и min границам.
        double d = fabs(Value);
        bool usign = Value >= 0.0;

        if (d >= this->FloatMax())
            return (usign) ? this->FloatMax() : -this->FloatMax();

        if (d <= this->FloatMin() && d > 0.0)
            return (usign) ? this->FloatMin() : -this->FloatMin();

        return float(Value);
    }
    qint32 CheckingInt32Borders(double Value) const
    {
        if (Value >= Int32Max()) return Int32Max();

        if (Value <= Int32Min()) return Int32Min();

        return qint32(Value);
    }


    double _value;
    bool _isreal;
};
Записан
blood_shadow
Гость
« Ответ #17 : Март 07, 2011, 11:25 »

У меня вопрос такой, что нужно написать вместо variant.canConvert<NNumeric>() для проверки на тип, чтобы в случае истины выполнить преобразование в NNumeric и выполнить дальнейшие действия?
есть такая штука с стандартного С++ typeid() , которая проверяет к какому классу принадлежит объект или указатель в данный момент времени
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #18 : Март 07, 2011, 12:00 »

Цитировать
Вот здесь второй вопрос. Есть момент, когда dt имеет указатель не инициализированный класс. Т.е. был объявлен указатель, но без вызова конструктора, потом его присвоили dt. Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?
Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 Смеющийся

Цитировать
не возникнет ли у меня утечки памяти?
Возникнет, обязательно)

Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
blood_shadow
Гость
« Ответ #19 : Март 07, 2011, 12:15 »


Вот здесь второй вопрос. Есть момент, когда dt имеет указатель не инициализированный класс. Т.е. был объявлен указатель, но без вызова конструктора, потом его присвоили dt. Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?


чувак читай книжки,
Код:
NDataTypes* dt;
это не объект, а просто указатель на объект базового класса,
создаются такие объекты через оператор new:
Код:
NDataTypes* dt = new NDataTypes(//то что в конструкторе)
я кажется понял из-за чего ошибка вылетает - оператору dynamic_cast нужно знать на какой объект в данный момент времени
ссылается указатель, на объект а не класс(!!!), а так как ты еще не создал объект то это и вызывает падение этого оператора,
класс - это описание(тип), объект- это физическая сущность(переменная)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #20 : Март 07, 2011, 12:28 »

Вот здесь второй вопрос. Есть момент, когда dt имеет указатель не инициализированный класс. Т.е. был объявлен указатель, но без вызова конструктора, потом его присвоили dt. Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?
Не видно как Вы объявились без конструктора, но если так то конечно никакие dynamic_cast и typeid работать не будут, т.к. именно код конструктора прописывает/заполняет RTTI (поэтому он и конструктор а не обычная ф-ция)

Др. замечания

if (dynamic_cast<NNumeric*>(dt) != NULL) {
        NNumeric numeric = dynamic_cast<NNumeric*>(dt);
"Не ленивый" программист пишет одну строку дважды  Улыбающийся

Код:
    //две строки ниже позволяют передать стековой переменной значения из указательной
    NNumeric(NNumeric* Numeric) {this->_id = Numeric->ID(); this->_constvalue = Numeric->ConstValue(); this->_name = Numeric->Name(); this->_isreal = Numeric->IsReal(); this->_value = Numeric->Value();}
    void operator= (NNumeric* a) {this->setID(a->ID()); this->setConstValue(a->ConstValue()); this->setName(a->Name()); this->setIsReal(a->IsReal()); this->setValue(a->Value());}
    //строк ниже поозволяет указательной переменной передать значения из стековой
    operator NNumeric*() {NNumeric* a = new NNumeric(); a->setID(this->ID()); a->setConstValue(this->ConstValue()); a->setName(this->Name()); a->setIsReal(this->IsReal()); a->setValue(this->Value()); return a;}

а) "this->" - так пишут на жабе, а здесь - ну можно, но если Вы хотите что-то подчеркнуть, а не просто так

б) не стоит делать описание класса таким неопрятным, если считаете что нужен  inline, а тело ф-ции не маленькое, то лучше  так (в хедере но за рамками класса)

Код
C++ (Qt)
inline NNumeric::NNumeric(NNumeric* num)
{
_id = num->ID();
...
}
Также в С/C++ принято имена переменных начинать с маленькой буквы

в) Трудно понять что Вы хотите сделать. Зачем нужен конструктор принимающий указатель? Гораздо естественнее перекрыть конструктор копирования. Зачем перекрывать оператор "*" в поисках (ненужных) приключений?

     //если идентификатор равен -1, значит это либо временная переменная, либо константное значение
        qint32 _id;
Смысл (примерно) понятен, но тогда может сделать базовый класс (без ID) а затем унаследоваться, добавив ID и имя. А так разбираться каждый раз выяснять кто там локальный/глобальный - утомительно
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Март 07, 2011, 12:45 »

Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 Смеющийся
Улыбающийся Ну я лично использую тот же метод что и Eten (ваять а там разберемся) и не считаю что это хуже. Просто надо более крытычно относиться к написанному и не бояться переделок. А "заложить фундамент" не всегда возможно, никогда не предусмотреть всего заранее.
Записан
brankovic
Гость
« Ответ #22 : Март 07, 2011, 13:38 »

Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ).

В принципе можно отловить на некоторых платформах, но вреда будет намного больше, чем пользы. Нельзя ли привести примеры типичного использования NNumeric? Просто непонятно, какую задачу решает определение операторов для указателя, почему нельзя так:

NNumeric a, b, c;
a = 12;
b = .7;
c = a + b;

зачем тут NNumeric*?
Записан
Eten
Гость
« Ответ #23 : Март 07, 2011, 13:43 »

Пардон за описание про указатель и объекты, увы не всегда точен в словах. Но на деле, для меня нет особой разницы, если код написан правильно, исполняет то машина, а не человек. Вот и забываю иногда про то, что пишу людям, а не компу.  Строит глазки

Спрашивал насчет утечек, т.к. раньше мне не доводилось использовать класс, который NNumeric* использует для хранения, а NNumeric для всего остального. Был бы у меня внутри указатель на что-то, которым я оперировал (как например в строках), тогда дело ясное. Без указателей, как у меня в NNumeric, также ясно. А когда храню в виде указателей и оперирую с объектом ввиде NNumeric, вот здесь озадачило. Просто я не понял, в каком месте у меня возникнет утечка. В самом NNumeric или в тех местах, где я храню.

Сделать преобразование в NNumeric*, создать конструктор копирования для NNumeric* и оператор присвоения NNumeric*, пришлось сделать из-за той же логики, что и с ссылкой на объект. И как показала практика, я не могу типу NNumeric* присвоить указатель на тип NNumeric и в дальнейшем его использовать, как указатель. Поэтому в преобразовании NNumeric к NNumeric* создается объект типа NNumeric*, ему передаются все значения, потом его же и выдаю. Т.о. значения объекта созданного в стеке передаются объекту, созданному в куче, и последний возвращается в том типе, который нужен. А оператор присвоения и конструктор NNumeric* созданы по тем же причинам, что создают с параметром NNumeric&. Тем более, что у меня операции над объектом происходят, не как с NNumeric*, а как с NNumeric. Отсюда и такой код, т.е. использовать NNumeric для действий над ним практичнее так, а хранить в отдельном месте в виде указателя NNumeric*, через указатель на базовый класс.
Записан
Eten
Гость
« Ответ #24 : Март 07, 2011, 14:11 »

Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ).

В принципе можно отловить на некоторых платформах, но вреда будет намного больше, чем пользы. Нельзя ли привести примеры типичного использования NNumeric? Просто непонятно, какую задачу решает определение операторов для указателя, почему нельзя так:

NNumeric a, b, c;
a = 12;
b = .7;
c = a + b;

зачем тут NNumeric*?

Класс NNumeric используется для хранения числа, операции проводятся уже в Си++ типах данных. Базовые классы у меня используются, как раз для хранения и иерархичности классов данных. Все операции NNumeric проводит так:
Код:
NNumeric a, b, c;
NNumeric *a1, *b2;
a = 12;
b = .7;
c = double(a) + double(b);
c = double(a) / double(b);
a1 = c;
b2 = new NNumeric(3.55);
a = b2;

NDataTypes* dt = new NDataTypes();
dt = b2;
b = dynamic_cast<NNumeric*>(dt);

Т.е. я преобразую NNumeric, для выполнения операций над его значением, в double, а потом использую автоматический конструктор с double, чтобы присвоить результат в "c", со всеми сопутствующими данными, которые использует сам движок. Преобразование в qint32 и float я использую для вывода значения, а также qint32 используется в тех операциях, где требуется работа только с целочисленными (например целочисленное деление). В классе нет перегруженных операторов арифметических операций, т.к. мне требуется удобная форма записи при обращению с памятью (получение из памяти и сохранение в память). Под памятью подразумевается место, где хранится экземпляр NNumeric. А т.к. у меня производится все именно, т.к. показано выше в коде и работа с базовыми классами и производными от них производится, через указатель (иначе я не встречал ни разу, у Страустрапа уж точно), то мне потребовалось сделать перегруженные операторы для указателей, чтобы сохранить работу класса как в коде выше.
Записан
brankovic
Гость
« Ответ #25 : Март 07, 2011, 14:17 »

А когда храню в виде указателей и оперирую с объектом ввиде NNumeric, вот здесь озадачило. Просто я не понял, в каком месте у меня возникнет утечка. В самом NNumeric или в тех местах, где я храню.

Код
C++ (Qt)
  dt = this->StackValue.pop();
 

указатель на NNumeric удалён из стека, но нигде нет вызова delete dt, это утечка.
Записан
Eten
Гость
« Ответ #26 : Март 07, 2011, 14:19 »

в) Трудно понять что Вы хотите сделать. Зачем нужен конструктор принимающий указатель? Гораздо естественнее перекрыть конструктор копирования. Зачем перекрывать оператор "*" в поисках (ненужных) приключений?
А на что можно напороться?!  В замешательстве У меня все работает без "приключений", но вот без переопределения работы с "*" в определенных ситуациях, код излишне увеличится. Так оказалось удобнее и компактней.  Подмигивающий
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #27 : Март 07, 2011, 14:54 »

Но на деле, для меня нет особой разницы, если код написан правильно, исполняет то машина, а не человек.
Здесь позвольте с Вами не согласиться. Как человек пишет - так и думает.

Сделать преобразование в NNumeric*, создать конструктор копирования для NNumeric* и оператор присвоения NNumeric*, пришлось сделать из-за той же логики, что и с ссылкой на объект. И как показала практика, я не могу типу NNumeric* присвоить указатель на тип NNumeric и в дальнейшем его использовать, как указатель. Поэтому в преобразовании NNumeric к NNumeric* создается объект типа NNumeric*, ему передаются все значения, потом его же и выдаю. Т.о. значения объекта созданного в стеке передаются объекту, созданному в куче, и последний возвращается в том типе, который нужен. А оператор присвоения и конструктор NNumeric* созданы по тем же причинам, что создают с параметром NNumeric&. Тем более, что у меня операции над объектом происходят, не как с NNumeric*, а как с NNumeric. Отсюда и такой код, т.е. использовать NNumeric для действий над ним практичнее так, а хранить в отдельном месте в виде указателя NNumeric*, через указатель на базовый класс.
Если объект имеет ID, то полагается что оно уникально и двух объектов с одним ID быть не должно. Обычно указатели на такие объекты хранятся в какой-то таблице. Ну и перекрывайте конструктор копирования, оператор присваивания и деструктор чтобы обеспечить уникальность этого ID. Другой путь - для локальных объектов использовать упрощенный класс который не имеет никакого ID и имени. А у Вас мутность великая, понять мудрено  Улыбающийся  Приведите максимально упрощенный пример где возникает проблема, никак не верится что нужны такие противоестественные хуки что Вы применяете.
Записан
brankovic
Гость
« Ответ #28 : Март 07, 2011, 15:03 »

Класс NNumeric используется для хранения числа, операции проводятся уже в Си++ типах данных. Базовые классы у меня используются, как раз для хранения и иерархичности классов данных. Все операции NNumeric проводит так:
Код
C++ (Qt)
NNumeric a, b, c;
NNumeric *a1, *b2;
a = 12;
b = .7;
c = double(a) + double(b);
c = double(a) / double(b);
a1 = c;
b2 = new NNumeric(3.55);
a = b2;
 
NDataTypes* dt = new NDataTypes();
dt = b2;
b = dynamic_cast<NNumeric*>(dt);
 

то же самое можно переписать без операторов от NNumeric*:

Код
C++ (Qt)
NNumeric a, b, c;
NNumeric a1, b2;
a = 12;
b = .7;
c = double(a) + double(b);
c = double(a) / double(b);
a1 = c;
b2 = 3.55;
a = b2;
 

такой код и читать легче, потому что не задумываешься над вопросами типа "зачем и как тут объекту присваивается указатель?". Не нужно вообще использовать new там, где это не нужно.

И совсем смущает последний кусок:
Код
C++ (Qt)
NNumeric *b2 = new NNumeric (1.21);
NDataTypes* dt = new NDataTypes();
dt = b2; //??! memory leak
b = *dynamic_cast<NNumeric*>(dt);
 
, так значение dt просто потеряется, лик.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #29 : Март 07, 2011, 15:25 »

Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 Смеющийся
Улыбающийся Ну я лично использую тот же метод что и Eten (ваять а там разберемся) и не считаю что это хуже. Просто надо более крытычно относиться к написанному и не бояться переделок. А "заложить фундамент" не всегда возможно, никогда не предусмотреть всего заранее.

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

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: 1 [2] 3 4 ... 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.28 секунд. Запросов: 22.