Russian Qt Forum

Программирование => С/C++ => Тема начата: Eten от Март 05, 2011, 06:35



Название: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 05, 2011, 06:35
В С# у меня с этим проблем не было, т.к. делал все через интерфейсы и у них была возможность сделать проверку перед преобразованием в производный. Точнее я делал преобразование базового типа в интерфейс, а уже потом интерфейсом проверял, могу ли я преобразовать. А также я мог с помощью такой проверки сделать разветвление для преобразования в тот тип, который содержит базовый. Например в Qt я могу использовать QVariant и у него canConvert<NNumeric> (например для проверки на тип NNumeric).

Т.е. NDataTypes является базовым по отношению к производным NNumeric, NString, NLogical.

Код:
void NEngine::Writeln(qint32 ID)
{
    NDataTypes* dt = new NDataTypes();
    QString str;
   
    if (ID == 0)
    {
        dt = this->StackValue.pop();
    }
    else
    {
        dt = this->Variables[ID-1];
    }

    if (variant.canConvert<NNumeric>()) {
        NNumeric* numeric = dynamic_cast<NNumeric*>(dt);
        str = QString(numeric);
    }

    if (variant.canConvert<NString>()) {
        NString string = dynamic_cast<NString*>(dt);
        str = string.Value();
    }

    if (variant.canConvert<NLogical>()) {
        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\>");
}

В условии с ID, dt передаются указатели, т.е. NDataTypes*. У меня вопрос такой, что нужно написать вместо variant.canConvert<NNumeric>() для проверки на тип, чтобы в случае истины выполнить преобразование в NNumeric и выполнить дальнейшие действия?


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 10:11
Советую срочно почитать литературу по с++. Вижу непонимание базовых классов и принципов наследования.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: _govorilka от Март 05, 2011, 10:38
Посмотрите исходник, как там реализована функция FwMLNode::cast<>(). Возможно, это вам поможет:
http://code.google.com/p/fireworks/source/browse/src/fwcore/fwml.h (http://code.google.com/p/fireworks/source/browse/src/fwcore/fwml.h)

Хотя Бьярн Страустрап, наверное поможет больше ;)...


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 05, 2011, 11:39
Код:
    if (variant.canConvert<NNumeric>()) {
        NNumeric* numeric = dynamic_cast<NNumeric*>(dt);
...

В условии с ID, dt передаются указатели, т.е. NDataTypes*. У меня вопрос такой, что нужно написать вместо variant.canConvert<NNumeric>() для проверки на тип, чтобы в случае истины выполнить преобразование в NNumeric и выполнить дальнейшие действия?
Ну намудрили, просто так

Код
C++ (Qt)
NNumeric* numeric = dynamic_cast<NNumeric*>(dt);
if (numeric != NULL)     // Ok, приведен
 ...
else      // это не NNumeric
...
 
Заметим что возвращаемый указатель numeric может быть и не равен dt, более того, тип указателя dt может и не наследовать NNumeric



Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 11:53
Цитировать
Чрезмерное применение понижающего приведения может служить признаком слишком бедного интерфейса базового класса
(А. Александреску, Г.Саттер)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 05, 2011, 12:09
Цитировать
Чрезмерное применение понижающего приведения может служить признаком слишком бедного интерфейса базового класса
(А. Александреску, Г.Саттер)
А что такое "понижающее приведение"? Судя по (тщательно заучиваемой) сентенции теоретиков это приведение базового типа к наследуемому, и вернее было бы назвать "повышающее". Ну так в данном примере об этом речи нет. Да и "может служить" не вызывает доверия. Ведь может и "не служить"


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 05, 2011, 12:13
Советую срочно почитать литературу по с++. Вижу непонимание базовых классов и принципов наследования.
В том то вся и проблема, что я еще вчера это сделал. Но вот про dynamic_cast ничего не нашел путного, тем более у Бьерна, что очень странно. Но видать не до конца разобрал или что-то упустил из внимания.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 12:19
Igors, хватит спорить только ради того, чтобы спорить. Я просто процитировал двух человек, которые в с++ разбираются намного лучше нас с тобой вместе взятых. Если с чем-то не согласен, обращайся к ним.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 05, 2011, 12:43
Так, к слову:
Возможности RTTI на dynamic_cast не заканчиваются)
Есть ещё такая штука, как typeid


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Авварон от Март 05, 2011, 12:46
двух человек, которые в с++ разбираются намного лучше нас с тобой вместе взятых.
Блин, сколько раз тебе говорил - не верь слепо прочитаному. Половина их мыслей неверна (причем незвисимо от того как они шарят - человеку просто надо было написать еще 1 предложение, вот он а бацнул что в голову пришло)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 13:04
Ну вот еще один спорщик. :) Я не слепо верю, а обдумываю и соглашаюсь или не соглашаюсь.

Закончим офтоп в этой ветке.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: pastor от Март 05, 2011, 13:36
А о чем вы спорите: о термине "понижающее приведение" или о самой сути высказывания? :)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 13:40
pastor, о сути, как я понял.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 05, 2011, 13:42
Моментик... Я за пивом сбегаю))


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 05, 2011, 13:44
Попкорн не забудь.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 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(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 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;
};


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: blood_shadow от Март 07, 2011, 11:25
У меня вопрос такой, что нужно написать вместо variant.canConvert<NNumeric>() для проверки на тип, чтобы в случае истины выполнить преобразование в NNumeric и выполнить дальнейшие действия?
есть такая штука с стандартного С++ typeid() , которая проверяет к какому классу принадлежит объект или указатель в данный момент времени


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 07, 2011, 12:00
Цитировать
Вот здесь второй вопрос. Есть момент, когда dt имеет указатель не инициализированный класс. Т.е. был объявлен указатель, но без вызова конструктора, потом его присвоили dt. Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ). Вот мне интересно, есть ли способ от него защититься или все же следить за тем, чтобы у указательных объектов перед из передачей указателю базового класса, использовались конструкторы?
Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 ;D

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



Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: blood_shadow от Март 07, 2011, 12:15

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


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


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 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 и имя. А так разбираться каждый раз выяснять кто там локальный/глобальный - утомительно


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 12:45
Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 ;D
:) Ну я лично использую тот же метод что и Eten (ваять а там разберемся) и не считаю что это хуже. Просто надо более крытычно относиться к написанному и не бояться переделок. А "заложить фундамент" не всегда возможно, никогда не предусмотреть всего заранее.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 07, 2011, 13:38
Но, в таком случае, при проверке dynamic_cast вылетает системная ошибка (точнее сигнал от системы). Через особые ситуации я его отловить не смог (даже, через catch(...) ).

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

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

зачем тут NNumeric*?


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 13:43
Пардон за описание про указатель и объекты, увы не всегда точен в словах. Но на деле, для меня нет особой разницы, если код написан правильно, исполняет то машина, а не человек. Вот и забываю иногда про то, что пишу людям, а не компу.  ::)

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

Сделать преобразование в NNumeric*, создать конструктор копирования для NNumeric* и оператор присвоения NNumeric*, пришлось сделать из-за той же логики, что и с ссылкой на объект. И как показала практика, я не могу типу NNumeric* присвоить указатель на тип NNumeric и в дальнейшем его использовать, как указатель. Поэтому в преобразовании NNumeric к NNumeric* создается объект типа NNumeric*, ему передаются все значения, потом его же и выдаю. Т.о. значения объекта созданного в стеке передаются объекту, созданному в куче, и последний возвращается в том типе, который нужен. А оператор присвоения и конструктор NNumeric* созданы по тем же причинам, что создают с параметром NNumeric&. Тем более, что у меня операции над объектом происходят, не как с NNumeric*, а как с NNumeric. Отсюда и такой код, т.е. использовать NNumeric для действий над ним практичнее так, а хранить в отдельном месте в виде указателя NNumeric*, через указатель на базовый класс.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 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. А т.к. у меня производится все именно, т.к. показано выше в коде и работа с базовыми классами и производными от них производится, через указатель (иначе я не встречал ни разу, у Страустрапа уж точно), то мне потребовалось сделать перегруженные операторы для указателей, чтобы сохранить работу класса как в коде выше.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 07, 2011, 14:17
А когда храню в виде указателей и оперирую с объектом ввиде NNumeric, вот здесь озадачило. Просто я не понял, в каком месте у меня возникнет утечка. В самом NNumeric или в тех местах, где я храню.

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

указатель на NNumeric удалён из стека, но нигде нет вызова delete dt, это утечка.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 14:19
в) Трудно понять что Вы хотите сделать. Зачем нужен конструктор принимающий указатель? Гораздо естественнее перекрыть конструктор копирования. Зачем перекрывать оператор "*" в поисках (ненужных) приключений?
А на что можно напороться?!  :-\ У меня все работает без "приключений", но вот без переопределения работы с "*" в определенных ситуациях, код излишне увеличится. Так оказалось удобнее и компактней.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 14:54
Но на деле, для меня нет особой разницы, если код написан правильно, исполняет то машина, а не человек.
Здесь позвольте с Вами не согласиться. Как человек пишет - так и думает.

Сделать преобразование в NNumeric*, создать конструктор копирования для NNumeric* и оператор присвоения NNumeric*, пришлось сделать из-за той же логики, что и с ссылкой на объект. И как показала практика, я не могу типу NNumeric* присвоить указатель на тип NNumeric и в дальнейшем его использовать, как указатель. Поэтому в преобразовании NNumeric к NNumeric* создается объект типа NNumeric*, ему передаются все значения, потом его же и выдаю. Т.о. значения объекта созданного в стеке передаются объекту, созданному в куче, и последний возвращается в том типе, который нужен. А оператор присвоения и конструктор NNumeric* созданы по тем же причинам, что создают с параметром NNumeric&. Тем более, что у меня операции над объектом происходят, не как с NNumeric*, а как с NNumeric. Отсюда и такой код, т.е. использовать NNumeric для действий над ним практичнее так, а хранить в отдельном месте в виде указателя NNumeric*, через указатель на базовый класс.
Если объект имеет ID, то полагается что оно уникально и двух объектов с одним ID быть не должно. Обычно указатели на такие объекты хранятся в какой-то таблице. Ну и перекрывайте конструктор копирования, оператор присваивания и деструктор чтобы обеспечить уникальность этого ID. Другой путь - для локальных объектов использовать упрощенный класс который не имеет никакого ID и имени. А у Вас мутность великая, понять мудрено  :)  Приведите максимально упрощенный пример где возникает проблема, никак не верится что нужны такие противоестественные хуки что Вы применяете.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 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 просто потеряется, лик.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 07, 2011, 15:25
Вот, вот, вместо того, чтобы сесть, тчательно подумать над архитектурой, проникнуться теорией, а уже потом имея фундамент, что то ваять, лучше тратить время на то, чтобы учится создавать костыли и костыли для костылей и т.д.
Герой на героине,
Героине на героине..
 ;D
:) Ну я лично использую тот же метод что и Eten (ваять а там разберемся) и не считаю что это хуже. Просто надо более крытычно относиться к написанному и не бояться переделок. А "заложить фундамент" не всегда возможно, никогда не предусмотреть всего заранее.

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


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 15:41
brankovic, спасибо, но я уже это понял, когда вы намекнули про утечку (этот момент я тоже исправил), поэтому там, где у себя лишним поставил деструктор для dt, его же и убрал. Работу с указателями привел потому, что вы спрашивали. А работа с указателем, которая была в них приведена, используется для получения NNumeric из тех мест, где он хранится в виде указателя на базовый класс. Если кто-то знает, как можно работать с производными и базовыми классами без указателей вообще, мне очень интересно посмотреть и узнать как это делается, т.к. Страустрап в своей книге все делал с помощью указателей.  ;)

Цитата: Igors
Здесь позвольте с Вами не согласиться. Как человек пишет - так и думает.
Я с вами полностью согласен, но по своему опыту знаю, что отношусь к тем людям, у которых понимание опережает формулирование мыслей. Из-за этого иногда возникают не состыковки. Т.е. я все могу понять, но не всегда в силах выразить это правильно на словах.  ::)

Цитата: Igors
Если объект имеет ID, то полагается что оно уникально и двух объектов с одним ID быть не должно. Обычно указатели на такие объекты хранятся в какой-то таблице. Ну и перекрывайте конструктор копирования, оператор присваивания и деструктор чтобы обеспечить уникальность этого ID. Другой путь - для локальных объектов использовать упрощенный класс который не имеет никакого ID и имени. А у Вас мутность великая, понять мудрено   :)  Приведите максимально упрощенный пример где возникает проблема, никак не верится что нужны такие противоестественные хуки что Вы применяете.
ID тут не причем, он и имя используется в других целях. Ясное дело, что ID у меня будут уникальными, но следить за этим я предпочел другим объектом, который его хранит. А вот насчет разделения на локи и глобалы я не совсем уверен, что будет лучше. Дело в том, что "область команд" (проще говоря список) исполняемых команд движком, имеет свои локальные переменные на которые она может указывать, путем перечисления их ID. Т.к. таких областей у меня может быть много, то проще разбивать области хранения под задачи, для которых они предназначены (как например, стек для временных переменных во время исполнения команды). Вдобавок, делать описанное вами будет излишне, т.к. у меня происходят обработка не только операции +, /, и т.д., а еще и операция присвоения. Тогда совсем логично, что ни ID, ни имя у временной переменной нет и быть не должно, а при присвоении ее значения присваиваемой переменной, остается только переменная, которой присвоили. Поэтому, ни операторы +, / и т.д., ни контроль ID мне внутри класса не нужен. Тем более, что проще объединить константные значения, переменные, временные переменные в один класс, т.к. по сути они одинаковы в своих значениях, а большего при операции над ними не требуется.

З.Ы.
Можеть я не так понял смысл вашего поста, но по мне вы начали думать о интерпретаторах, что несколько выходит за рамки темы.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 15:48
Не спорю, что всего заранее не предусмотреть. Но, "заложить фундамент" я понимаю как некий основной теор. минимум: как вообще другие с подобными проблемами справлялись, есть ли отточенные решения для этой ситуации, какие инструменты ит.д.
И под архитектурой я вовсе не имею ввиду окончательный вариант, но некий интерплей между объектами, их обязанности,  иерархия, какие то свойства основные..
Короче, хотел выразить мысль, что картина вырисовывается не из-за понятий, а из взаимоотношений между ними.. Как то так))
К тому же, хуже от того, чтобы вначале подумать, взять листочек с ручкой, почеркать, походить, покурить точно не будет) 
(разве что от покурить)))
   
Я тоже продумываю все на листке бумаги, но всегда знаю, что может получиться криво. Ибо вначале, никак без этого, всегда где-нибудь не хватить знаний или опыта.  ;) Но вот иерархия для класса NNumeric у меня вышла такая, какая она есть. И я не нашел пока причин ее переделывать иначе, мне пока этого варианта хватает. Все остальное подчиненно именно постановке задачи и того, что требуется. Т.е., если написаны переопределенные операторы, значит при исходном (без них) варианте, они мне понадобились, чтобы сохранить исходный вариант записи при добавлении операций с "*". Все, как учил дядька Страустрап.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 16:07
Не спорю, что всего заранее не предусмотреть. Но, "заложить фундамент" я понимаю как некий основной теор. минимум: как вообще другие с подобными проблемами справлялись, есть ли отточенные решения для этой ситуации, какие инструменты ит.д.
Хммм.. ну как сказать, "дело тонкое". Мне часто приходится делать задачи которые имеют аналоги, т.е. уже не раз делались. Но это совсем не Assistant где уже готовые/отточенные решения. Конечно, всегда хочется "прикрутить готовое" - но вот получается редко  :) Смотришь - вроде исходники (часто open-source) грамотные, ладно, рискну. А через неделю-две начинаешь понимать что (несмотря на "формальную грамотность") программист тупенько передрал такую-то статью. А что оно работает "до первого милиционера", что никакие граничные случаи не отрабатываются, да и вообще сам алгоритм слабоват - так его это не волновало. Чтобы не быть голословным, вот примерчик (Вы templae любите)

http://www.csie.ntu.edu.tw/~cyy/courses/rendering/pbrt-2.00/html/octree_8h_source.html (http://www.csie.ntu.edu.tw/~cyy/courses/rendering/pbrt-2.00/html/octree_8h_source.html)

Где с первого взгляда все гладко/хорошо - но это не так


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 07, 2011, 16:10
Не спорю, что всего заранее не предусмотреть. Но, "заложить фундамент" я понимаю как некий основной теор. минимум: как вообще другие с подобными проблемами справлялись, есть ли отточенные решения для этой ситуации, какие инструменты ит.д.
И под архитектурой я вовсе не имею ввиду окончательный вариант, но некий интерплей между объектами, их обязанности,  иерархия, какие то свойства основные..
Короче, хотел выразить мысль, что картина вырисовывается не из-за понятий, а из взаимоотношений между ними.. Как то так))
К тому же, хуже от того, чтобы вначале подумать, взять листочек с ручкой, почеркать, походить, покурить точно не будет)  
(разве что от покурить)))
  
Я тоже продумываю все на листке бумаги, но всегда знаю, что может получиться криво. Ибо вначале, никак без этого, всегда где-нибудь не хватить знаний или опыта.  ;) Но вот иерархия для класса NNumeric у меня вышла такая, какая она есть. И я не нашел пока причин ее переделывать иначе, мне пока этого варианта хватает. Все остальное подчиненно именно постановке задачи и того, что требуется. Т.е., если написаны переопределенные операторы, значит при исходном (без них) варианте, они мне понадобились, чтобы сохранить исходный вариант записи при добавлении операций с "*". Все, как учил дядька Страустрап.  ;)
Не думаете ли Вы, что задуманная Вами архитектура единствена?
Я хочу сказать, что нужно учится выбирать правильные решения, а не решения, которые направлены на обход граблей.
Я тоже согласен с brankovic, что введение таких сомнительных операторов только запутывают логику и программу - это грабли, на которые кто-нить да наступит..

А можно, если не сложно, описать для чего всё это нужно и как по Вашему это всё работает и почему это должно быть именно так?
Вобщем описание проблемы не могли бы изложить?  


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 07, 2011, 16:19
Не спорю, что всего заранее не предусмотреть. Но, "заложить фундамент" я понимаю как некий основной теор. минимум: как вообще другие с подобными проблемами справлялись, есть ли отточенные решения для этой ситуации, какие инструменты ит.д.
Хммм.. ну как сказать, "дело тонкое". Мне часто приходится делать задачи которые имеют аналоги, т.е. уже не раз делались. Но это совсем не Assistant где уже готовые/отточенные решения. Конечно, всегда хочется "прикрутить готовое" - но вот получается редко  :) Смотришь - вроде исходники (часто open-source) грамотные, ладно, рискну. А через неделю-две начинаешь понимать что (несмотря на "формальную грамотность") программист тупенько передрал такую-то статью. А что оно работает "до первого милиционера", что никакие граничные случаи не отрабатываются, да и вообще сам алгоритм слабоват - так его это не волновало. Чтобы не быть голословным, вот примерчик (Вы templae любите)

http://www.csie.ntu.edu.tw/~cyy/courses/rendering/pbrt-2.00/html/octree_8h_source.html (http://www.csie.ntu.edu.tw/~cyy/courses/rendering/pbrt-2.00/html/octree_8h_source.html)

Где с первого взгляда все гладко/хорошо - но это не так

Я бы не стал это использовать.. Я всегда с большим подозрением отношусь к подобным конструкциям:
Код
C++ (Qt)
template <typename NodeData> struct OctNode {
00037     OctNode() {
00038         for (int i = 0; i < 8; ++i)
00039             children[i] = NULL;
00040     }
00041     ~OctNode() {
00042         for (int i = 0; i < 8; ++i)
00043             delete children[i];
00044     }
00045     OctNode *children[8];
00046     vector<NodeData> data;
00047 };
 
Когда вот так просто открытые указатели объявляют..
Это не в традициях C++
хотя впрочем этот класс у них явно нигде не пресутствует.. Не знаю, тут разбираться надо..
Просто это первое, что в глаза попало..


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 16:22
но по своему опыту знаю, что отношусь к тем людям, у которых понимание опережает формулирование мыслей. Из-за этого иногда возникают не состыковки.
Насколько могу судить о себе, я принадлежу к именно такому типу людей  :) Но как ни крути, а задачу формулировать надо. Вы же даете/публикуете только свои выводы, они могут быть верны или нет, но совершенно непонятно к какой задаче/цели они относятся  :)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 16:47
Когда вот так просто открытые указатели объявляют..
Это не в традициях C++
хотя впрочем этот класс у них явно нигде не пресутствует.. Не знаю, тут разбираться надо..
Просто это первое, что в глаза попало..
Ну то "дело вкуса". Я спокойно отношусь к открытым членам класса, если Вы нет - никто не мешает сделать это private, код-то открыт. А вот принципиально что эта реализация (как и многие др) имеет жестокие пробои в ф-циональности и, по существу, пригодна лишь для студенческого курсового (так, абы було, отчитаться)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 07, 2011, 17:08
brankovic, спасибо, но я уже это понял, когда вы намекнули про утечку (этот момент я тоже исправил), поэтому там, где у себя лишним поставил деструктор для dt, его же и убрал. Работу с указателями привел потому, что вы спрашивали.

Хотелось бы увидеть не саму работу с указателями а место, где она обязательно необходима. Вот, например, я храню

std::vectror <NNumeric *> stack;

я могу вместо этого спокойно использовать

std::vector <NNumeric> stack;

Но пусть даже будет NNuberic *, тогда функцию pop () можно написать примерно так:

Код
C++ (Qt)
NNumeric Stack::pop ()
{
  NNumeric tmp = *m_stack.back ();
  delete m_stack.back ();
  m_stack.pop_back ();
  return tmp;
}
 

не хочется навязываться со своим мнением, но я всё же не понимаю, что удобного в указателях. ИМХО лучше обойтись без них, чем создавать путаницу всякими операторами (а перегрузка операторов и конструкторы от нетривиальных агрументов это всегда путаница).

То есть вопрос такой: по какой причине для NNumeric вообще нужно хоть раз использовать оператор new?


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 17:18
Вот, например, я храню

std::vectror <NNumeric *> stack;

я могу вместо этого спокойно использовать

std::vector <NNumeric> stack;
Если Eten связался с ID и "уникальностью" экземпляров - то нет, практически он вынужден работать по указателю. Конечно не беда, но зачем создавать причудливые конструкции?


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 17:50
Не думаете ли Вы, что задуманная Вами архитектура единствена?
Я хочу сказать, что нужно учится выбирать правильные решения, а не решения, которые направлены на обход граблей.
Я тоже согласен с brankovic, что введение таких сомнительных операторов только запутывают логику и программу - это грабли, на которые кто-нить да наступит..

А можно, если не сложно, описать для чего всё это нужно и как по Вашему это всё работает и почему это должно быть именно так?
Вобщем описание проблемы не могли бы изложить?  
Единственная врятли, т.к. идея всегда одна, вариантов море. Надо только понимать, что выбрав один вариант, как основную линию, всегда практичней ее придерживаться, нежели брать обрывки из различных вариантов. Попытка взять все самое лучшее из других вариантов, по сути еще один вариант.  ;)

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

Для начало повторюсь, что класс NNumeric (ровно как и другие) чисто хранит в себе значение и дополнительную информацию нужную движку-исполнителю. Который в свою очередь обеспечивает работу с такими типами данных, как NNumeric. Т.е. движок - это исполнитель входного кода в виде команд. Он осуществляет операции с экземплярами NNumeric и хранит из в виде указателей на базовый класс. Базовые классы используются не только для хранения, но для группирования классов по иерархии, т.к. есть классы сложнее NNumeric и использующие его для хранения чисел. Т.к. класс NNumeric используется для хранения и конструктор копирования по ссылке и также оператор присваивания по умолчанию копируют члены из экземпляра  одного в другой, то намного практичнее было сделать класс NNumeric именно принимающим значения, а не выполняющим переопределенные операторы для арифметики. Т.к. он спокойно может выдать свое значение с учетом типа, в который преобразуется. А дальше обычные действия. Т.к. в основном операции идут, через тип double и не могут пересечь ее границ (из-за границ установленных в классе, так и из-за границ самого типа float, т.к. получаем числа из файлов в виде 4 байт), то удобней применять его в качестве параметра автоматического конструктора и осуществлять контроль границ уже внутри класса. Сами типы float и qint32 также выдаются, но чисто для записи в файл или на вывод, но qint32 также используется в целочисленных операциях вместо double. Естественно для контроля целое/не целое есть _isreal, с помощью которого движок осуществляет правильное преобразование значений в нужный тип данных. Т.о. преобладает использование класса NNumeric  без указателя. Но, т.к. нигде не было встречено работы базовых классов с производными без указатели в СИ++, то необходимо использовать указатели на базовый класс. Но указатели на базовый класс приводятся к указателям на класс NNumeric, а переопределенные операторы (т.е. те, что имеют в классе) и работают с экземпляром NNumeric, а не указателем на класс NNumeric. Поэтому потребовалось сохранить работу с экземплярами NNumeric, а указатели использовать только чисто для хранения, но оперировать только лишь экземплярами NNumeric. Тем более, что передавая т.о., как описано в классе NNumeric мы не трогаем хранящиеся переменные, константные значения и временные переменные (хотя эти врятли, т.к. они попадают только в QStack). Т.к. при таком подходе обрывается связь с тем место, откуда был взят экземпляр, как если бы я работал через QVariant. А мне именно это и нужно, чтобы не затрагивать место хранения. С другой стороны, просто так указателю на класс не присвоишь экземпляр класса, т.к. работать с указателем ссылающимся на экземпляр класса объявленного не в куче, а в стеке вызывает очевидные проблемы. Следовательно, нужно по отдельно использовать все set функции для передачи хранящейся информации из экземпляра в указатель на класса, предварительно вызвав конструктор для указателя на класс. Этот указатель мы и сохраняем в указатель на базовый класс. Получается следующая ситуация, у нас много операций с экземплярами и нам придется повторить такое же кол-во раз, сколько и самих экземпляров используется, отсюда могут полезть ошибки и получится использование одного и того же кода два и более раз. Поэтому, практичнее сократить форму записи прописав все это в преобразование в NNumeric*, т.к. при наличии подобного переопределенного оператора он используется при попытке экземпляр класса присвоить указателю на этот класс или базовый для него. Т.о. мне практически ничего не надо было переписывать в функции матем. операций. Но одна сторона медали, а теперь другая. Т.к. для вывода и прочих операций опять используются только экземпляры класса NNumeric, то и здесь указатели не нужны. Но, хранится то у нас это все в указателях на базовый класс и даже после приведения к классу, это опять же указатель на класс, а нам нужны экземпляры. Опять же возникнет многократное вызывание функций set у класса NNumeric и опять тоже самое, что и в случае с передачей значений из указателя в экземпляр класса. Опять же все это прописываем в классе перегружая конструтор копирования, который по сути просто копирует значения, но внутри себя, и оператор присваивания, который по сути выполняет тоже самое. Но есть одно "но", однако здесь тоже самое, что и с конструктором копирования по ссылке и такому же оператору присвоения. Т.е. в одних случая идет присвоение, в других инициализация. Вот и получается, что таким переопределением определенных операторов и конструкторов я могу спокойно пользоваться экземплярами класса, а при передачи их в место хранения они сами выдадут указатель на класс созданный в куче, а не в стеке. А также при взятии из места хранения мне нет нужды исписывать километры строк для передачи значений в экземпляр класса. При этом не затрагивается как место хранения при работе с указателями, так и сами значения при выполнении операций над экземплярами класса. Говоря иначе, указатели для хранения с использованием базовых классов, ибо без указателей здесь никак.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 17:52
Вот, например, я храню

std::vectror <NNumeric *> stack;

я могу вместо этого спокойно использовать

std::vector <NNumeric> stack;
Если Eten связался с ID и "уникальностью" экземпляров - то нет, практически он вынужден работать по указателю. Конечно не беда, но зачем создавать причудливые конструкции?
Igors, ну вы же не будете думать, что я взял и создал иерархию классов только для одного класса NNumeric?! У меня там хранятся все производные от NDataTypes классы.

И честно говоря, у меня в программе они так быть использованы, как у вас не могут. Они не для такого применения задуманы.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 18:02
Ладно, приложу еще одну функцию:
Код
C++ (Qt)
void NeorgekEngine::NEngine::MathematicalOperations(ushort Modifikator)
{
   NNumeric arg0, arg1, arg2;
   NDataTypes* dt;
 
   switch (Modifikator)
   {
       case 2:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (!arg1.IsReal() && !arg2.IsReal())
           {
                arg0.setIsReal(false);
                arg0.setValue(double(arg1) + double(arg2));
           }
           else
               arg0 = double(arg1) + double(arg2);
 
           this->StackValue.push(arg0);
       break;
       case 3:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (!arg1.IsReal() && !arg2.IsReal())
           {
               arg0.setIsReal(false);
               arg0.setValue(double(arg1) - double(arg2));
           }
           else
               arg0 = double(arg1) - double(arg2);
 
           this->StackValue.push(arg0);
       break;
       case 4:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (!arg1.IsReal() && !arg2.IsReal())
           {
               arg0.setIsReal(false);
               arg0.setValue(double(arg1) * double(arg2));
           }
           else
               arg0 = double(arg1) * double(arg2);
 
           this->StackValue.push(arg0);
       break;
       case 5:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           //if (arg2 == 0)
 
           if (!arg1.IsReal() && !arg2.IsReal())
           {
               arg0.setIsReal(false);
               arg0.setValue(double(arg1) / double(arg2));
           }
           else
               arg0 = double(arg1) / double(arg2);
 
           this->StackValue.push(arg0);
       break;
       case 6:
           dt = this->StackValue.pop();
           arg0 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0++;
           arg0.setID(-1);
           arg0.setConstValue(false);
           arg0.setName("");
 
           this->StackValue.push(arg0);
       break;
       case 7:
           dt = this->StackValue.pop();
           arg0 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0--;
           arg0.setID(-1);
           arg0.setConstValue(false);
           arg0.setName("");
 
           this->StackValue.push(arg0);
       break;
       case 8:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0.setIsReal(false);
           arg0.setValue((ldiv(qint32(arg1), qint32(arg2)).quot));
 
           this->StackValue.push(arg0);
       break;
       case 9:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0.setIsReal(false);
           arg0.setValue((ldiv(qint32(arg1), qint32(arg2)).rem));
 
           this->StackValue.push(arg0);
       break;
       case 10:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (!arg1.IsReal() && !arg2.IsReal())
           {
                arg0.setIsReal(false);
                arg0.setValue(pow(double(arg1),double(arg2)));
           }
           else
               arg0 = pow(double(arg1),double(arg2));
 
           this->StackValue.push(arg0);
       break;
       case 11:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (!arg1.IsReal())
           {
               arg0.setIsReal(false);
               arg0.setValue(sqrt(double(arg1)));
           }
           else
               arg0 = sqrt(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 12:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0.setIsReal(arg1.IsReal());
           arg0.setValue(std::abs(double(arg1)));
 
           this->StackValue.push(arg0);
       break;
       case 13:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (double(arg1) < double(arg2)) arg0 = arg1; else arg0 = arg2;
 
           arg0.setID(-1);
           arg0.setConstValue(false);
           arg0.setName("");
 
           this->StackValue.push(arg0);
       break;
       case 14:
           dt = this->StackValue.pop();
           arg2 = dynamic_cast<NNumeric*>(dt);
           delete dt;
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           if (double(arg1) > double(arg2)) arg0 = arg1; else arg0 = arg2;
 
           arg0.setID(-1);
           arg0.setConstValue(false);
           arg0.setName("");
 
           this->StackValue.push(arg0);
       break;
       case 15:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           srand ( time(NULL) );
           arg0.setIsReal(false);
           arg0.setValue(qint32(rand() % qint32(arg1)));
 
           this->StackValue.push(arg0);
       break;
       case 16:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = log10(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 17:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = log(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 18:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = exp(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 19:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = cos(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 20:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = sin(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 21:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = tan(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 22:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = 1.0/tan(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 23:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = acos(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 24:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = asin(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 25:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = atan(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 26:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = 1.0/atan(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 27:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = floor(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 28:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = ceil(double(arg1));
 
           this->StackValue.push(arg0);
       break;
       case 29:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = arg1.round();
 
           this->StackValue.push(arg0);
       break;
       case 30:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = arg1.fractional();
 
           this->StackValue.push(arg0);
       break;
       case 31:
           dt = this->StackValue.pop();
           arg1 = dynamic_cast<NNumeric*>(dt);
           delete dt;
 
           arg0 = arg1.integer();
 
           this->StackValue.push(arg0);
       break;
       case 32:
 
           srand ( time(NULL) );
           arg0 = double(rand() % 1001)/1000.0;
 
           this->StackValue.push(arg0);
       break;
   }
}
 

Вопрос, как это (см. код выше) можно записать без применения тех трех макроопределений с NNumeric*, а ведь это только малая часть движка?  ;)

З.Ы.
Честно, я все никак не могу понять, почему вы их называете хаками, ведь я ничего противоестественного не делаю с самими операторами и конструктором, т.е. использую их как по обычному без переопределенний, всего лишь сократил себе лишние строки в коде.  ???


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 07, 2011, 19:36
Честно, я все никак не могу понять, почему вы их называете хаками, ведь я ничего противоестественного не делаю с самими операторами и конструктором, т.е. использую их как по обычному без переопределенний, всего лишь сократил себе лишние строки в коде.  ???
Диалог в переполненном автобусе

Цитировать
- Цигане, посунься будь ласка
- Я афро-американец, а не цыган
- O! То якi в вас мають бути цигани? 
Если Вы считаете что так Вы "ничего плохого" не делаете, то что ж будет когда сделаете?  :)

case 2:
            dt = this->StackValue.pop();
            arg2 = dynamic_cast<NNumeric*>(dt);
            delete dt;
            dt = this->StackValue.pop();
            arg1 = dynamic_cast<NNumeric*>(dt);
            delete dt;
 
            if (!arg1.IsReal() && !arg2.IsReal())
            {
                 arg0.setIsReal(false);
                 arg0.setValue(double(arg1) + double(arg2));
            }
            else
                arg0 = double(arg1) + double(arg2);
 
            this->StackValue.push(arg0);
        break;
Нормальный программист "рефлекторно" стремится записать это одной строкой, напр.

Код
C++ (Qt)
case NNumeric::operation_plus:
stack.push(stack.pop_value() + stack.pop_valie());
break;
 
Где pop_value просто так

Код
C++ (Qt)
NNumeric Stack::pop_value( void )
{
if (!size()) throw "stack error";
NNumeric * temp = pop();
NNumeric value(*temp);
delete temp;
return value;
}
 

И еще. Не хотелось начинать неприятный разговор, но приходится. Перлы типа

Цитировать
case 2: // видимо Вам еще не приходилось "откапывать" это 2 и вспоминать маму написавшего
..
case 32:

StackValie (с большой буквы и совсем не "value")
С полным правом могут расцениваться как прямое неуважение к собеседникам и к форуму вообще. Так можно писать "на первенство двора", но не в приличном обществе. Извольте оформить все как положено, иначе - будьте здоровы .



Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 07, 2011, 20:00
Не думаете ли Вы, что задуманная Вами архитектура единствена?
Я хочу сказать, что нужно учится выбирать правильные решения, а не решения, которые направлены на обход граблей.
Я тоже согласен с brankovic, что введение таких сомнительных операторов только запутывают логику и программу - это грабли, на которые кто-нить да наступит..

А можно, если не сложно, описать для чего всё это нужно и как по Вашему это всё работает и почему это должно быть именно так?
Вобщем описание проблемы не могли бы изложить?  
Единственная врятли, т.к. идея всегда одна, вариантов море. Надо только понимать, что выбрав один вариант, как основную линию, всегда практичней ее придерживаться, нежели брать обрывки из различных вариантов. Попытка взять все самое лучшее из других вариантов, по сути еще один вариант.  ;)

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

Для начало повторюсь, что класс NNumeric (ровно как и другие) чисто хранит в себе значение и дополнительную информацию нужную движку-исполнителю. Который в свою очередь обеспечивает работу с такими типами данных, как NNumeric. Т.е. движок - это исполнитель входного кода в виде команд. Он осуществляет операции с экземплярами NNumeric и хранит из в виде указателей на базовый класс. Базовые классы используются не только для хранения, но для группирования классов по иерархии, т.к. есть классы сложнее NNumeric и использующие его для хранения чисел. Т.к. класс NNumeric используется для хранения и конструктор копирования по ссылке и также оператор присваивания по умолчанию копируют члены из экземпляра  одного в другой, то намного практичнее было сделать класс NNumeric именно принимающим значения, а не выполняющим переопределенные операторы для арифметики. Т.к. он спокойно может выдать свое значение с учетом типа, в который преобразуется. А дальше обычные действия. Т.к. в основном операции идут, через тип double и не могут пересечь ее границ (из-за границ установленных в классе, так и из-за границ самого типа float, т.к. получаем числа из файлов в виде 4 байт), то удобней применять его в качестве параметра автоматического конструктора и осуществлять контроль границ уже внутри класса. Сами типы float и qint32 также выдаются, но чисто для записи в файл или на вывод, но qint32 также используется в целочисленных операциях вместо double. Естественно для контроля целое/не целое есть _isreal, с помощью которого движок осуществляет правильное преобразование значений в нужный тип данных. Т.о. преобладает использование класса NNumeric  без указателя. Но, т.к. нигде не было встречено работы базовых классов с производными без указатели в СИ++, то необходимо использовать указатели на базовый класс. Но указатели на базовый класс приводятся к указателям на класс NNumeric, а переопределенные операторы (т.е. те, что имеют в классе) и работают с экземпляром NNumeric, а не указателем на класс NNumeric. Поэтому потребовалось сохранить работу с экземплярами NNumeric, а указатели использовать только чисто для хранения, но оперировать только лишь экземплярами NNumeric. Тем более, что передавая т.о., как описано в классе NNumeric мы не трогаем хранящиеся переменные, константные значения и временные переменные (хотя эти врятли, т.к. они попадают только в QStack). Т.к. при таком подходе обрывается связь с тем место, откуда был взят экземпляр, как если бы я работал через QVariant. А мне именно это и нужно, чтобы не затрагивать место хранения. С другой стороны, просто так указателю на класс не присвоишь экземпляр класса, т.к. работать с указателем ссылающимся на экземпляр класса объявленного не в куче, а в стеке вызывает очевидные проблемы. Следовательно, нужно по отдельно использовать все set функции для передачи хранящейся информации из экземпляра в указатель на класса, предварительно вызвав конструктор для указателя на класс. Этот указатель мы и сохраняем в указатель на базовый класс. Получается следующая ситуация, у нас много операций с экземплярами и нам придется повторить такое же кол-во раз, сколько и самих экземпляров используется, отсюда могут полезть ошибки и получится использование одного и того же кода два и более раз. Поэтому, практичнее сократить форму записи прописав все это в преобразование в NNumeric*, т.к. при наличии подобного переопределенного оператора он используется при попытке экземпляр класса присвоить указателю на этот класс или базовый для него. Т.о. мне практически ничего не надо было переписывать в функции матем. операций. Но одна сторона медали, а теперь другая. Т.к. для вывода и прочих операций опять используются только экземпляры класса NNumeric, то и здесь указатели не нужны. Но, хранится то у нас это все в указателях на базовый класс и даже после приведения к классу, это опять же указатель на класс, а нам нужны экземпляры. Опять же возникнет многократное вызывание функций set у класса NNumeric и опять тоже самое, что и в случае с передачей значений из указателя в экземпляр класса. Опять же все это прописываем в классе перегружая конструтор копирования, который по сути просто копирует значения, но внутри себя, и оператор присваивания, который по сути выполняет тоже самое. Но есть одно "но", однако здесь тоже самое, что и с конструктором копирования по ссылке и такому же оператору присвоения. Т.е. в одних случая идет присвоение, в других инициализация. Вот и получается, что таким переопределением определенных операторов и конструкторов я могу спокойно пользоваться экземплярами класса, а при передачи их в место хранения они сами выдадут указатель на класс созданный в куче, а не в стеке. А также при взятии из места хранения мне нет нужды исписывать километры строк для передачи значений в экземпляр класса. При этом не затрагивается как место хранения при работе с указателями, так и сами значения при выполнении операций над экземплярами класса. Говоря иначе, указатели для хранения с использованием базовых классов, ибо без указателей здесь никак.
Млин, я честно пречитал это раза три, но нифига не понял  :(
Лучше опишите суть задачи, которую Вы пытаетесь реализовать.. (как можно проще и понятнее, на пальцах можно сказать)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 07, 2011, 23:05
case 2:
            dt = this->StackValue.pop();
            arg2 = dynamic_cast<NNumeric*>(dt);
            delete dt;
            dt = this->StackValue.pop();
            arg1 = dynamic_cast<NNumeric*>(dt);
            delete dt;
 
            if (!arg1.IsReal() && !arg2.IsReal())
            {
                 arg0.setIsReal(false);
                 arg0.setValue(double(arg1) + double(arg2));
            }
            else
                arg0 = double(arg1) + double(arg2);
 
            this->StackValue.push(arg0);
        break;
Нормальный программист "рефлекторно" стремится записать это одной строкой, напр.

Код
C++ (Qt)
case NNumeric::operation_plus:
stack.push(stack.pop_value() + stack.pop_valie());
break;
 
Где pop_value просто так

Код
C++ (Qt)
NNumeric Stack::pop_value( void )
{
if (!size()) throw "stack error";
NNumeric * temp = pop();
NNumeric value(*temp);
delete temp;
return value;
}
 

И еще. Не хотелось начинать неприятный разговор, но приходится. Перлы типа

Цитировать
case 2: // видимо Вам еще не приходилось "откапывать" это 2 и вспоминать маму написавшего
..
case 32:

StackValie (с большой буквы и совсем не "value")
С полным правом могут расцениваться как прямое неуважение к собеседникам и к форуму вообще. Так можно писать "на первенство двора", но не в приличном обществе. Извольте оформить все как положено, иначе - будьте здоровы .
Не знаю, откуда у вас взялся StackValie, но у меня его в коде отродясь не было, иначе не работало б тогда. И приведенные здесь коды взяты с исходников, так что опечаток подобных у меня нету.  ;) Насчет упомянки про case 2, каюсь совсем забыл. Вот как раз выдержка из памятки:
Код:
Модификатор	Комментарий
2 +
3 -
4 *
5 /
6 ++
7 --
8 целочисленное деление
9 % (остаток от целочисленного деления)
10 ^
11 квадратный корень
12 модуль числа
13 минимальное число
14 максимальное число
15 целочисленный рандом от 0 и до m-1
16 log10
17 ln
18 exp
19 cos
20 sin
21 tg
22 ctg
23 arccos
24 arcsin
25 arctg
26 arcctg
27 округление в сторону нуля до целого
28 округление в большую сторону до целого
29 округление до целого
30 выделение дробной части
31 отсечение дробной части
32 рандом от 0 до 1
В плане не уважения, ни кого обидеть не хотел, но что-то мне приходится часто таскать из темы в тему свой класс. Правильно ли я поступаю?  :-\

Да, кстати, а почему вы мне все время указывает на то, что я с большой буквы начинаю? В этом нет ничего плохого, т.к. есть два общепринятых стиля. Один из них, который указывали вы и другие, а другой я сам использую. Что-то я не припомню, что был только один стиль.  ;)

Насчет сократить код до одной строки мысль интересная, но не везде годная. Это я еще в функции MathematicalOperations обработку на особые ситуации не прописывал (надо до этого прописать и реализовать кое-что еще). Например в case 7, это у вас никак не выйдет. Но все равно спасибо.  ;)

З.Ы.
Про автобус рассмешило, но ситуация с хаками для все равно, что вопрос для человека из древности: "Земля вокруг Солнца или Солнце вокруг Земли движется".  ::)

З.З.Ы.
m_ax, я понимаю, что возможно написал сложновато или увлекся, но сегодня уже никак, давайте завтра. Утро, вечера мудренее.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 08, 2011, 00:01
всего лишь сократил себе лишние строки в коде.

Это как раз согласен, тоже не люблю лишнего писать. Но этот конструктор со * ничего не сокращает, а только усложняет. Вот есть три строки:

Код
C++ (Qt)
dt = this->StackValue.pop();
arg1 = dynamic_cast<NNumeric*>(dt);
delete dt;
 

их можно переписать так (безо всякого NNumeric(NNumeric*)):

Код
C++ (Qt)
arg1 = *dynamic_cast<NNumeric*>(this->StackValue.pop());
 

одну звёздочку добавил, 2 строчки убрал. А по-хорошему надо вынести в отдельную функцию, как Igors и советовал и везде вместо троек этих страшных останется:

Код
C++ (Qt)
  arg1 = pop_value ();
 

Тут можно будет и проверку на тип организовать, и на пустой стек, и всё в одном месте. А так, как сейчас, чуть в стеке что не то попадётся и всё, access violation.

Edit: речь не о том, что конструктор NNumeric (NNumeric *) "неправильный", потому что его нет в книжке или ещё что-то. Речь о том, что он не приносит пользы. Я не против извращений как таковых, я против извращений от которых нет никакого удовольствия.

Edit: код номер 2 конечно бред, хорошо, что Eten меня понял и так :)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 08, 2011, 07:12
brankovic, вот так бы сразу и сказали.  ;) Я, честно говоря, помню о работе с указателями, но что-то восприятие надо мной шутку сыграло.  ::) Да, конструктор действительно оказался уже не нужен (в классе его убрал), а вот и та функция (пока она будет без  дополнительных проверок, это буду писать позднее), о которой вы говорили:

Код:
NNumeric NEngine::PopNumeric()
{
    NDataTypes* dt;
    NNumeric arg;
    dt = this->StackValue.pop();
    arg = *dynamic_cast<NNumeric*>(dt);
    delete dt;
    return arg;
}


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 08, 2011, 07:56
В общем спасибо всем за помощь, есть чему поучиться у других.  :)

А также обещанное про краткое описание задачи. Задача тут следующая. Мне нужно производить работу над значениями NNumeric, в одних случаях через функции типа get/set, в других случаях через операторы преобразования и конструкторы для удобной формы записи, определенного контроля над значениями внутри класса и сокращения кода, чтобы вся работа производилась только через экземпляры класса. А этих экземпляров класса NNumeric, учитывая что исполнитель - это сути своей автомат и работу он производит автоматически, используется, при операциях с ними, достаточно мало. Только хранение в памяти они в виде экземпляров могут занимать много места (в чем-то могу здесь ошибаться, но как ни крути, а с указателями по части хранения и работы с памятью будет проще и быстрее). Т.о. нужно реализовать класс NNumeric так, чтобы можно было, спрятав работу экземпляра класса с указателем на класс, использовать экземпляр этого класса везде, где это необходимо. Т.е. сохранить работу с классом NNumeric в программе, в виде работы с экземплярами класса NNumeric. ИМХО, так проще для моей программы, а заодно можно спокойно пользоваться базовыми классами (и ляпов не сделать где-нибудь, т.е. класс сам контролирует передачу данных, т.к. это и есть его основная задача) не сильно изменяя сам код программы (по сути дела там произошли всего лишь небольшие изменения, а не переписывание всего кода или его большой части).

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

З.Ы.
Еще проще я написать не смогу и не хотелось бы больше возвращаться к этому, т.к. я уже почти все разобрал в этой теме. Остался только вопрос "Динамическая информация о типе" (сейчас над этим работаю). Это единственное, что осталось у меня. Заранее хочу сказать, что у меня и в мыслях нету никого обижать, но если мы будем обсуждать класс NNumeric дальше, то эта тема начнет отнимать у меня время, которое я хочу потратить на свой проект. Если уж вас сильно стало интересовать чем я занимаюсь, то напишите мне в личку, дам вам ссылку на сайт с информацией, но не гарантирую что она вам будет понятна (там хранится мой черновик).  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: lenny от Март 08, 2011, 12:21
никакие dynamic_cast и typeid работать не будут, т.к. именно код конструктора прописывает/заполняет
Можно поподробнее? Или ссылку на материал описывающий данный процесс.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 08, 2011, 15:41
Перечитал Бьерна и поискал информацию по typeid (http://www.cplusplus.com/reference/std/typeinfo/type_info/). В общем, проверка на совпадение производным классом, указателя на базовый класс, для меня вышла такой (в функции Writeln, см. ответ 15 (http://www.prog.org.ru/index.php?topic=17062.msg114431#msg114431)):
Код:
if (typeid(NNumeric) == typeid(*dt)) {
     NNumeric numeric = *dynamic_cast<NNumeric*>(dt);
     str = QString(numeric);
}

Соответственно строка с "#include <typeinfo>" стоит на положенном месте.  ;)

До этого она была такой:
Код:
if (dynamic_cast<NNumeric*>(dt) != NULL) {
      NNumeric numeric = dynamic_cast<NNumeric*>(dt);
      str = QString(numeric);
}

Больше вопросов по этой теме не имею.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 08, 2011, 15:43
никакие dynamic_cast и typeid работать не будут, т.к. именно код конструктора прописывает/заполняет
Можно поподробнее? Или ссылку на материал описывающий данный процесс.
Ну эти познания я почерпнул (довольно давно) из описания "Турбо Паскаль 5.5 (Введение в объектно-ориентированное программирование)"   :)

А вообще, насколько я знаю, формат RTTI закрыт, т.е. каждый компилятор может реализовывать как ему хочется - разумно. Как я понял (балуясь/экспериментируя в свободное от работы время в отладчике), RTTI хранит 2 значения:

1) указатель на сам объект
2) смещение данного типа от начала объекта

В связи с этим
Код
C++ (Qt)
class A { int mDataA; };
class B { int mDataB; };
 
B * A2B( A * a )
{
return dynamic_cast <B *> (a);
}
 
Эта ф-ция может вернуть не NULL


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 08, 2011, 16:16
Eten, я вовсе не сторонник "формального" подхода и "чеканного следования прынцыпам ООП" - скорее наоборот. Но "всему же есть границы" как писал классик. А Вы их попираете/насилуете на каждом шагу. Ну вот например
Код
C++ (Qt)
           if (!arg1.IsReal() && !arg2.IsReal())
           {
                arg0.setIsReal(false);
                arg0.setValue(double(arg1) + double(arg2));
           }
           else
               arg0 = double(arg1) + double(arg2);
 
Да какого же <..> какая-то ф-ция (посторонка) лезет в детали реализации класса, указывает ему setIsReal и.т..п? Стопудово это сам класс (или его friend операторы) должны решать. Все "потроха" должны быть спрятаны (называется умным словом "инкапсуляция"). Ладно бы еще это один раз (ну сачканул, бывает), но ведь Вы уверенно размножили это для всех операций десятки(!) раз. Я не хочу Вас обидеть, но программирование "через copy/paste" просто унизительно. Работа программиста есть придумывание логических конструкций, а не "долбление".


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 08, 2011, 16:31
Igors, применение IsReal, как и их запись, через setIsReal мне нужна при работе движка с NNumeric. Здесь же, есть момент, когда мне необходимо делать вызовы set-функций для записи целых чисел. А применение IsReal в условии это то, что необходимо движку при его работе. Честно, я не нашел способа короче.  ;)

З.Ы.
Смотря, что прятать в классе.  ;)

З.Ы.
Да, кстати подобные вызовы стоят у меня не где попало, а где это необходимо (например в функции мат. операций, логики и работы со строками, а также при записи переменных в память). ИМХО, класс достаточно инкапсулирован в соответствии с той задачей, для которой он был создан. ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 08, 2011, 17:30
Да, кстати подобные вызовы стоят у меня не где попало, а где это необходимо (например в функции мат. операций, логики и работы со строками, а также при записи переменных в память). ИМХО, класс достаточно инкапсулирован в соответствии с той задачей, для которой он был создан. ;)
Ой, ну вот не надо прятаться за специфику задачи и рассказывать сказки "про белого бычка"  :) Вопрос о "типе результата" прекрасно решается на уровне класса, напр.

Код
C++ (Qt)
// friend
NNumeric operator + ( const NNumeric & arg0, const NNumeric & arg1 )
{
bool useReal = arg0.IsReal() || arg1.IsReal();
return NNumeric(arg0.mValue + arg1.mValue, useReal);
}
 
NNumeric::NNumeric( double val, bool useReal )
{
mIsReal = useReal;
CheckLimits(val);
mValue = val;
}
 
Я не помню точно имена членов класса - но не суть

Edit: да и вообще по-хорошему метод IsReal должен быть private


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 08, 2011, 17:40
Ладно, убедили.

Но скажу сразу, что не подумал как-то об этом, изначально код то у меня был другой (т.е. явно не было видно). И постоянно что-то переписывая, можно и не заметить, как придешь к такому.  ::)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 08, 2011, 18:10
brankovic, вот так бы сразу и сказали.  ;) Я, честно говоря, помню о работе с указателями, но что-то восприятие надо мной шутку сыграло.  ::) Да, конструктор действительно оказался уже не нужен (в классе его убрал), а вот и та функция (пока она будет без  дополнительных проверок, это буду писать позднее), о которой вы говорили:

Код:
NNumeric NEngine::PopNumeric()
{
    NDataTypes* dt;
    NNumeric arg;
    dt = this->StackValue.pop();
    arg = *dynamic_cast<NNumeric*>(dt);
    delete dt;
    return arg;
}
А зачем здесь dt удалять?? Или так задумано, что при каждом вызове PopNumeric удалять очередной элемент в StackValue? Это не хорошо..
Короче полный даркнэсс, как говорит один мой знакомый)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 08, 2011, 18:36
Код:
NNumeric NEngine::PopNumeric()
{
    NDataTypes* dt;
    NNumeric arg;
    dt = this->StackValue.pop();
    arg = *dynamic_cast<NNumeric*>(dt);
    delete dt;
    return arg;
}
А зачем здесь dt удалять?? Или так задумано, что при каждом вызове PopNumeric удалять очередной элемент в StackValue? Это не хорошо..
Короче полный даркнэсс, как говорит один мой знакомый)
Ну почему нехорошо? "pop" предполагает что ответственность за "снятый" элемент возьмет на себя кто-то другой, может и удалить, это нормально. Другое дело вот это порно
Код
C++ (Qt)
   dt = this->StackValue.pop();
 
Зачем же так "серпом по нежному месту"?  Если имя не придумалось, просто доливаете артикль, напр
Код
C++ (Qt)
   dt = theStack.pop();
 
Просто и со вкусом


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 08, 2011, 18:47
Цитировать
Ну почему нехорошо? "pop" предполагает что ответственность за "снятый" элемент возьмет на себя кто-то другой, может и удалить, это нормально.
В смысле? Возвращает же функция не указатель...
Даже если бы и указатель, то delete dt порушит весь объект к чёрту..
Т.е. попробуйте вызвать эту функцию дважды PopNumeric() интересно, что получится)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 08, 2011, 19:27
Цитировать
Ну почему нехорошо? "pop" предполагает что ответственность за "снятый" элемент возьмет на себя кто-то другой, может и удалить, это нормально.
В смысле? Возвращает же функция не указатель...
Даже если бы и указатель, то delete dt порушит весь объект к чёрту..
Т.е. попробуйте вызвать эту функцию дважды PopNumeric() интересно, что получится)
Цитировать
- Нет-нет, я не туда попал
- Да как же не туда - да именно сюда
(глупые стихи случайно застрявшие в памяти  :))
"pop" снимает из очереди LIFO (стек) но ничего не обещает об удалении. Последовательный вызов 2 и более pop приведет к снятию 2 и более тех кто был на стеке - это нормально. Конечно Вы это знаете, просто заработались/заскок  :)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 08, 2011, 19:44
Короче полный даркнэсс, как говорит один мой знакомый)

Просто Eten на тёмной стороне силы. Но это не значит, что он не станет великим программистом ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 09, 2011, 07:06
Фух, только что переписал до конца (разумеется выспавшись  ;) ) на стиль Си++ оформление везде в своей проге, плюс сделал полную инкапсуляцию класса NNumeric. Заодно, оказалось, что проблем при работе с местом хранения (т.е. запись в места хранения) полная инкапсуляция мне не создает. Плюс ко всему этому, обеспечил класс NNumeric статическими математическими функциями, которые для него допустимы исходя из ТЗ программы. Т.е. работает, как и раньше, только код стал удобным, читабельным и компактным.

Базовые классы для NNumeric:
Код
C++ (Qt)
#include <QString>
 
   class NDataTypes
   {
   public:
       NDataTypes() {_id = -1; _name = "";}
       virtual ~NDataTypes() { }
 
       qint32 id() const {return _id;}
       void setId(qint32 id) {_id = id;}
       QString name() const {return _name;}
       void setName(QString name) {if (name != NULL) _name = name; else _name = "";}
 
   protected:
       //если идентификатор равен -1, значит это либо временная переменная, либо константное значение
       qint32 _id;
       //константные или временные значения имен не имеют, только переменные.
       QString _name;
 
   };
 
   class NSimpleDataTypes : public NDataTypes
   {
   public:
       NSimpleDataTypes() : NDataTypes() {_constValue = false;}
 
       bool constValue() const {return _constValue;}
       void setConstValue(bool constValue) {_constValue = constValue;}
 
   protected:
       //это указывает на константное значение
       bool _constValue;
   };
 

Сам класс NNuemric:
Код
C++ (Qt)
class NNumeric : public NSimpleDataTypes
{
public:
 
   NNumeric(double value = 0)
       : NSimpleDataTypes()
   {
       _isTypeInteger = false;
       setValue(value);
   }
 
   NNumeric(double value, bool isTypeInteger)
           : NSimpleDataTypes()
   {
       setisTypeInteger(isTypeInteger);
       setValue(value);
   }
 
 
   //целочисленное деление
   static NNumeric nnDiv(NNumeric a, NNumeric b)
   {
       return NNumeric(ldiv(qint32(a), qint32(b)).quot, true);
   }
 
   //% (остаток от целочисленного деления)
   static NNumeric nnMod(NNumeric a, NNumeric b)
   {
       return NNumeric(ldiv(qint32(a), qint32(b)).rem, true);
   }
 
   //возведение в степень
   static NNumeric nnPow(NNumeric a, NNumeric b)
   {
       return NNumeric(pow(double(a), double(b)), a.isTypeInteger() && b.isTypeInteger());
   }
 
   //квадратный корень
   static NNumeric nnSqrt(NNumeric a)
   {
       return NNumeric(sqrt(double(a)), a.isTypeInteger());
   }
 
   //модуль числа
   static NNumeric nnAbs(NNumeric a)
   {
       return NNumeric(std::abs(double(a)), a.isTypeInteger());
   }
 
   //минимальное число
   static NNumeric nnMin(NNumeric a, NNumeric b)
   {
       double d1 = double(a), d2 = double(b);
       return d1 < d2 ? NNumeric(d1, a.isTypeInteger()) : NNumeric(d2, b.isTypeInteger());
   }
 
   //максимальное число
   static NNumeric nnMax(NNumeric a, NNumeric b)
   {
       double d1 = double(a), d2 = double(b);
       return d1 > d2 ? NNumeric(d1, a.isTypeInteger()) : NNumeric(d2, b.isTypeInteger());
   }
 
   //десятичный логарифм
   static NNumeric nnLog10(NNumeric a)
   {
       return NNumeric(log10(double(a)));
   }
 
   //натуральный логарифм
   static NNumeric nnLog(NNumeric a)
   {
       return NNumeric(log(double(a)));
   }
 
   //экспонента в степени "a"
   static NNumeric nnExp(NNumeric a)
   {
       return NNumeric(exp(double(a)));
   }
 
   //косинус
   static NNumeric nnCos(NNumeric a)
   {
       return NNumeric(cos(double(a)));
   }
 
   //синус
   static NNumeric nnSin(NNumeric a)
   {
       return NNumeric(sin(double(a)));
   }
 
   //тангенс
   static NNumeric nnTan(NNumeric a)
   {
       return NNumeric(tan(double(a)));
   }
 
   //арктангенс
   static NNumeric nnCtan(NNumeric a)
   {
       return NNumeric(1.0 / tan(double(a)));
   }
 
   //арккосинус
   static NNumeric nnACos(NNumeric a)
   {
       return NNumeric(acos(double(a)));
   }
 
   //арксинус
   static NNumeric nnASin(NNumeric a)
   {
       return NNumeric(asin(double(a)));
   }
 
   //арктангенс
   static NNumeric nnATan(NNumeric a)
   {
       return NNumeric(atan(double(a)));
   }
 
   //арккатангенс
   static NNumeric nnACtan(NNumeric a)
   {
       return NNumeric(1.0 / atan(double(a)));
   }
 
   //округление в сторону нуля до целого
   static NNumeric nnFloor(NNumeric a)
   {
       return NNumeric(floor(double(a)));
   }
 
   //округление в большую сторону до целого
   static NNumeric nnCeil(NNumeric a)
   {
       return NNumeric(ceil(double(a)));
   }
 
   //округление до целого
   static NNumeric nnRound(NNumeric a)
   {
       return NNumeric(floor(double(a) + 0.5));
   }
 
   //выделение дробной части
   static NNumeric nnFractional(NNumeric a)
   {
       double d = double(a);
       return d < 0.0 ? NNumeric(d - ceil(d)) : NNumeric(d - floor(d));
   }
 
   //отсечение дробной части
   static NNumeric nnInteger(NNumeric a)
   {
       double d = double(a);
       return d < 0.0 ? NNumeric(ceil(d)) : NNumeric(floor(d));
   }
 
   //целочисленный рандом от 0 и до a-1
   static NNumeric nnRandom(NNumeric a)
   {
       srand ( time(NULL) );
       return NNumeric(rand() % qint32(a), false);
   }
 
   //рандом от 0 до 1
   static NNumeric nnRnd()
   {
       srand ( time(NULL) );
       return NNumeric(double(rand() % 1001)/1000.0);
   }
 
 
   NNumeric operator+(NNumeric b)
   {
       return NNumeric(this->value() + b.value(), this->isTypeInteger() && b.isTypeInteger());
   }
 
   NNumeric operator-(NNumeric b)
   {
       return NNumeric(this->value() - b.value(), this->isTypeInteger() && b.isTypeInteger());
   }
 
   NNumeric operator*(NNumeric b)
   {
       return NNumeric(this->value() * b.value(), this->isTypeInteger() && b.isTypeInteger());
   }
 
   NNumeric operator/(NNumeric b)
   {
       return NNumeric(this->value() / b.value(), this->isTypeInteger() && b.isTypeInteger());
   }
 
   void operator++(int)
   {
       if (isTypeInteger())
           _value = checkingInt32Borders(_value + 1);
       else
           _value = checkingFloatBorders(_value + 1);
   }
 
   void operator--(int)
   {
       if (isTypeInteger())
           _value = checkingInt32Borders(_value - 1);
       else
           _value = checkingFloatBorders(_value - 1);
   }
 
 
   operator QString() {return QString::number(_value);}
 
   operator float() {return checkingFloatBorders(value());}
 
   operator qint32() {return checkingInt32Borders(_value);}
 
   //оператор ниже позволяет передать указателю значения из экземпляра класса
   operator NNumeric*()
   {
       NNumeric* pnn = new NNumeric();
 
       pnn->setId(id());
       pnn->setConstValue(constValue());
       pnn->setName(name());
       pnn->setisTypeInteger(isTypeInteger());
       pnn->setValue(value());
 
       return pnn;
   }
 
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 >= floatMax())
           return (usign) ? floatMax() : -floatMax();
 
       if (d <= floatMin() && d > 0.0)
           return (usign) ? floatMin() : -floatMin();
 
       return float(value);
   }
 
   qint32 checkingInt32Borders(double value) const
   {
       if (value >= int32Max()) return int32Max();
 
       if (value <= int32Min()) return int32Min();
 
       return qint32(value);
   }
 
 
   void setisTypeInteger(bool isTypeInteger) {_isTypeInteger = isTypeInteger;}
 
   //Если истинно, то значение принимается и выдается (при печати числа на экране)
   // с отсечение дробной части числа и соблюдением границ типа qint32.
   bool isTypeInteger() const {return _isTypeInteger;}
 
   void setValue(double value)
   {
       if (isTypeInteger())
           value = checkingInt32Borders(value);
       else
           value = checkingFloatBorders(value);
 
       _value = value;
   }
 
   double value() const {return double(checkingFloatBorders(_value));}
 
 
   operator double() {return double(checkingFloatBorders(value()));}
 
 
   double _value;
   bool _isTypeInteger;
};
 

Функция мат. операций:
Код
C++ (Qt)
void NEngine::mathematicalOperations(ushort Modifikator)
{
   NNumeric arg;
 
   switch (Modifikator)
   {
       //сумма
       case 2:
           theStack.push(popNumeric() + popNumeric());
       break;
 
       //разность
       case 3:
           theStack.push(popNumeric() - popNumeric());
       break;
 
       //умножение
       case 4:
           theStack.push(popNumeric() * popNumeric());
       break;
 
       //деление
       case 5:
           theStack.push(popNumeric() / popNumeric());
       break;
 
       //инкремент
       case 6:
           arg = popNumeric();
           arg++;
           theStack.push(arg);
       break;
 
       //декремент
       case 7:
           arg = popNumeric();
           arg--;
           theStack.push(arg);
       break;
 
       //целочисленное деление
       case 8:
           theStack.push(NNumeric::nnDiv(popNumeric(), popNumeric()));
       break;
 
       //% (остаток от целочисленного деления)
       case 9:
           theStack.push(NNumeric::nnMod(popNumeric(), popNumeric()));
       break;
 
       //возведение в степень
       case 10:
           theStack.push(NNumeric::nnPow(popNumeric(), popNumeric()));
       break;
 
       //квадратный корень
       case 11:
           theStack.push(NNumeric::nnSqrt(popNumeric()));
       break;
 
       //модуль числа
       case 12:
           theStack.push(NNumeric::nnAbs(popNumeric()));
       break;
 
       //минимальное число
       case 13:
           theStack.push(NNumeric::nnMin(popNumeric(), popNumeric()));
       break;
 
       //максимальное число
       case 14:
           theStack.push(NNumeric::nnMax(popNumeric(), popNumeric()));
       break;
 
       //целочисленный рандом от 0 и до m-1
       case 15:
           theStack.push(NNumeric::nnRandom(popNumeric()));
       break;
 
       //log10
       case 16:
           theStack.push(NNumeric::nnLog10(popNumeric()));
       break;
 
       //ln
       case 17:
           theStack.push(NNumeric::nnLog(popNumeric()));
       break;
 
       //exp
       case 18:
           theStack.push(NNumeric::nnExp(popNumeric()));
       break;
 
       //cos
       case 19:
           theStack.push(NNumeric::nnCos(popNumeric()));
       break;
 
       //sin
       case 20:
           theStack.push(NNumeric::nnSin(popNumeric()));
       break;
 
       //tg
       case 21:
           theStack.push(NNumeric::nnTan(popNumeric()));
       break;
 
       //ctg
       case 22:
           theStack.push(NNumeric::nnCtan(popNumeric()));
       break;
 
       //arccos
       case 23:
           theStack.push(NNumeric::nnACos(popNumeric()));
       break;
 
       //arcsin
       case 24:
           theStack.push(NNumeric::nnASin(popNumeric()));
       break;
 
       //arctg
       case 25:
           theStack.push(NNumeric::nnATan(popNumeric()));
       break;
 
       //arcctg
       case 26:
           theStack.push(NNumeric::nnACtan(popNumeric()));
       break;
 
       //округление в сторону нуля до целого
       case 27:
           theStack.push(NNumeric::nnFloor(popNumeric()));
       break;
 
       //округление в большую сторону до целого
       case 28:
           theStack.push(NNumeric::nnCeil(popNumeric()));
       break;
 
       //округление до целого
       case 29:
           theStack.push(NNumeric::nnRound(popNumeric()));
       break;
 
       //выделение дробной части
       case 30:
           theStack.push(NNumeric::nnFractional(popNumeric()));
       break;
 
       //отсечение дробной части
       case 31:
           theStack.push(NNumeric::nnInteger(popNumeric()));
       break;
 
       //рандом от 0 до 1
       case 32:
           theStack.push(NNumeric::nnRnd());
       break;
   }
}
 

Код
C++ (Qt)
void NEngine::writeln(qint32 id)
{
   NDataTypes* dt;
   QString str;
 
 
   if (id == 0)
   {
       dt = theStack.pop();
   }
   else
   {
       dt = variables[id-1];
   }
 
   if (typeid(NNumeric) == typeid(*dt))
   {
       NNumeric numeric = *dynamic_cast<NNumeric*>(dt);
       str = QString(numeric);
   }
 
   if (typeid(NString) == typeid(*dt))
   {
       NString string = *dynamic_cast<NString*>(dt);
       str = string.value();
   }
 
   if (typeid(NLogical) == typeid(*dt))
   {
       NLogical logical = *dynamic_cast<NLogical*>(dt);
       str = QString(logical);
   }
 
   delete dt;
   screen.setText(screen.text()+str+"<br\>");
}
 

Полагаю, что еще лучше не упрячешь. А инкремент и декремент, операции исключительного рода, поэтому их никак не свернуть и там они еще требует выполнение записи обратно (пока не реализовал).

З.Ы.
Мне уже скоро NNuemric сниться начнут.  ::)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 09, 2011, 12:25
Фух, только что переписал до конца (разумеется выспавшись  ;) )
..
З.Ы.
Мне уже скоро NNuemric сниться начнут.  ::)
Должны, это нормально когда программист работает над задачей.
Переписали - намного лучше, но все равно, еще очень "рыхло" (как в ранних романах Бальзака).

NSimpleDataTypes() : NDataTypes() {_constValue = false;}
Дефаулт конструктор базового класса сам вызовется, поэтому упоминать его не нужно

case 2:
Так писать западло, Вы создаете кучу потенциальных проблем. Есть просто enum, его можно  освоить за 5-10 минут. Закоренелые С-шники предпочитают #define - тоже приемлемо но НИКОГДА не должно проходить "просто число"

с id и name нужно разбираться, здесь требуется постановка


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 09, 2011, 13:08

NSimpleDataTypes() : NDataTypes() {_constValue = false;}
Дефаулт конструктор базового класса сам вызовется, поэтому упоминать его не нужно
А как насчет того, что в Си++ все переменные нужно определять?! Будет ли у меня также, как было описано в конструкторе.


case 2:
Так писать западло, Вы создаете кучу потенциальных проблем. Есть просто enum, его можно  освоить за 5-10 минут. Закоренелые С-шники предпочитают #define - тоже приемлемо но НИКОГДА не должно проходить "просто число"

с id и name нужно разбираться, здесь требуется постановка

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

Насчет id и name это вы правы, но для этого мне еще нужно воссоздать некоторое кол-во классов, чтобы постановка приобрела более четкую картину представления и ее можно было бы дать прочесть другим. Но на это потребуется не мало времени.

Переписали - намного лучше, но все равно, еще очень "рыхло" (как в ранних романах Бальзака).
А можете подсказать, в чем рыхловатость?

Щас я не смогу увидеть всей рыхловатости, т.к. создаю поэтапно, но зная направление своего проекта. И при построении других классов и применения этого, я смогу заметить что-то и исправить (с напильником в руках) найденный недочет. Но некоторые подсказки, даже навскидку могут подсказать мне направления, с помощью которых я смогу увидеть то, чего можеть быть и не смог. Или то, о чем мог и не подозревать.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 09, 2011, 13:42
А можете подсказать, в чем рыхловатость?

лучшее враг хорошего. Для вашего уровня всё отлично. Понимание тонкостей придёт с опытом

P.S.: если Igors сказал всего лишь рыхловатый, то в моём представлении это где-то между 4+ и 5- :)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Eten от Март 09, 2011, 14:03
а-а-а-а, а то я уже подумал много чего. Но теперь буду заранее помнить, что если сначала (при первом представлении класса) не вышло, еще не значит, что потом не смогу инкапсулировать. Т.е. сначало у меня это не получалось, т.к. зашел немного не с той стороны, но в итоге все равно добился желаемого.  ;)


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: _govorilka от Март 10, 2011, 08:25
Насчет id и name это вы правы, но для этого мне еще нужно воссоздать некоторое кол-во классов, чтобы постановка приобрела более четкую картину представления и ее можно было бы дать прочесть другим. Но на это потребуется не мало времени.

Код:
QHash<int, QString> idToName;
QHash<QString, int> nameToID;

В чем сложность?..


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: _govorilka от Март 10, 2011, 08:32
Если в класс NDataTypes добавишь виртуальную функцию:

Код
C++ (Qt)
    QString toString() const = 0;
 

То вот этот кусок кода:

Код
C++ (Qt)
 
if (typeid(NNumeric) == typeid(*dt))
   {
       NNumeric numeric = *dynamic_cast<NNumeric*>(dt);
       str = QString(numeric);
   }
 
   if (typeid(NString) == typeid(*dt))
   {
       NString string = *dynamic_cast<NString*>(dt);
       str = string.value();
   }
 
   if (typeid(NLogical) == typeid(*dt))
   {
       NLogical logical = *dynamic_cast<NLogical*>(dt);
       str = QString(logical);
   }
 
 

Сможешь переписать так:

Код
C++ (Qt)
   QString str = dt->toString();
 


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: voral от Март 10, 2011, 09:49
Не совсем по теме, но:
а) "this->" - так пишут на жабе, а здесь - ну можно, но если Вы хотите что-то подчеркнуть, а не просто так
Я то же так пишу. Цель: выделить то, что относится к классу от каких то "локальных" переменных. Можно сказать на всякий случай, что б потом если придется вернуться к коду через некий промежуток времени незадумываться


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 10, 2011, 10:07
ИМХО, ненужное засорение кода.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Igors от Март 10, 2011, 11:00
А можете подсказать, в чем рыхловатость?
Ну вот например

if (typeid(NNumeric) == typeid(*dt))
    {
        NNumeric numeric = *dynamic_cast<NNumeric*>(dt);
        str = QString(numeric);
    }
 
    if (typeid(NString) == typeid(*dt))
    {
        NString string = *dynamic_cast<NString*>(dt);
        str = string.value();
    }

- зачем привлекать и typeid и еще dynamic_cast? Это вещи разные, здесь лучше обойтись одной,

- добавить else чтобы код не гонял воздух, если к одному типу привелись, другие приведения уже ни к чему проверять

- неясно зачем нужно возвращать numeric по значению, чем не устраивает просто указатель для получения строки?

- согласен с _govorilka что иметь метод типа toString гораздо проще/естественнее (может и обобщить удастся). Конструктор QString(numeric) не вызывается необходимостью

- если цель вернуть строку, то менять объекты мы не собираемся, значит это надо подчеркнуть словом const

Поэтому поприятнее так
Код
C++ (Qt)
const NNumeric * numeric = dynamic_cast<const NNumeric*> (dt);
if (numeric)
  str = numeric->toString();
else {
..
 
Или, как указал _govorilka , сделать toString методом базового класса тогда вместо ветвистого else всего одна проверка
Код
C++ (Qt)
const BaseClass * base = dynamic_cast<const BaseClass *> (dt); // возможно и это не нужно
if (base)
  str = base->toString();
 
Пусть не критично/обязательно, но в этом есть смысл.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 10, 2011, 11:04
ИМХО, ненужное засорение кода.
Я тож так не пишу, но
Могу привести одну ситуацию, где так писать целесообразно:
Например у нас есть некоторый класс, который имеет закрытую переменную, назовём её value:
Код
C++ (Qt)
class A
{
public:
   void setValue(int value) { this->value = value; }
private:
   int value;
}
 
 
Однако у меня такое исключено, поскольку перед закрытыми переменными ставлю _


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 10, 2011, 11:10
Или, как указал _govorilka , сделать toString методом базового класса тогда вместо ветвистого else всего одна проверка
Код
C++ (Qt)
const BaseClass * base = dynamic_cast<const BaseClass *> (dt); // возможно и это не нужно
if (base)
  str = base->toString();
 
Пусть не критично/обязательно, но в этом есть смысл.
Так даже более того, не нужно будет делать никакого приведения, поскольку toString() будет относится к интерфейсу.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: brankovic от Март 10, 2011, 11:16
Однако у меня такое исключено, поскольку перед закрытыми переменными ставлю _

не рекомендуется стандартом, имена с _ и __ зараезервированы для стандартной библиотеки. Поля класса называю на m, типа m_name. Некоторые ставят _ в конце имени: name_. ИМХО любой способ лучше, чем this->.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Пантер от Март 10, 2011, 12:02
Согласен с brankovic, ставлю после имени _. Перед именем не рекомендуют ставить _ из-за возможной коллизии имен с макросами.


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: Авварон от Март 10, 2011, 12:26
пишу через d_ptr-_-


Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: _govorilka от Март 10, 2011, 12:30
С функцией в базовом классе можно переписать и без dynamic_cast:

Код
C++ (Qt)
void NEngine::writeln(qint32 id)
{
   NDataTypes* dt = (id ? variables[id - 1] : theStack.pop());
   if(dt)
  {
        screen.setText(screen.text()+ dt->toString() + "<br\>");
        delete dt;
  }
}
 



Название: Re: Как узнать о типе, до выполнения dynamic_cast?
Отправлено: m_ax от Март 10, 2011, 12:32
Однако у меня такое исключено, поскольку перед закрытыми переменными ставлю _

не рекомендуется стандартом, имена с _ и __ зараезервированы для стандартной библиотеки. Поля класса называю на m, типа m_name. Некоторые ставят _ в конце имени: name_. ИМХО любой способ лучше, чем this->.

Согласен с brankovic, ставлю после имени _. Перед именем не рекомендуют ставить _ из-за возможной коллизии имен с макросами.

Вот ведь, век живи - век учись)) Всё, перехожу обратно на m_