Название: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Eten от Март 27, 2011, 10:06
Я разобрался с полиморфизмом и наследованием. И на данный момент я перешел на использование интерфейсов, вместо старого базового класса. Т.е. у меня все хранится в базовом интерфейсе (естественно через указатели), а не в базовом классе, т.к. по сути он в себе хранит переданный ему класс (по адресу указателя видно). И по смыслу интерфейсами лучше. Т.е. способ работы от этого не изменился. typeid по конечному классу (NNumeric, NString, NLogical) у меня нормально проверяет, а вот попытки проверить на базовый класс NSimpleDataTypes (т.е. попытка проверки конечного класса на принадлежность к NSimpleDataTypes, как к группе) не увенчиваются успехом. Когда все проверил и еще раз перечитал нужную литературу понял, что typeid подходит только чисто для проверки класса на тип. Т.е. типы от которых он наследовался я проверить не смогу. В общем, что нужно и как правильно задать что-то, чтобы можно было создать проверку класса на принадлежность к группе классов? Например, NNumeric принадлежит к группе NSimpleDataTypes, т.к. является производным от этого класса. Заранее скажу, что я понимаю и помню о возможности нагрузить классы доп. полями для реализации подобного, но хочу узнать от толковых людей, есть ли возможность достичь желаемого результата не замусоривая т.о. классы.
Интерфейсы: C++ (Qt) class INDataTypes { public: virtual ~INDataTypes() { } virtual qint32 id() const = 0; virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0; protected: //если идентификатор равен -1, значит это либо временная переменная, либо константное значение qint32 _id; //константные или временные значения имен не имеют, только переменные. QString _name; }; class INSimpleDataTypes : public INDataTypes { public: virtual QString valueToQString() const = 0; protected: //это указывает на константное значение bool _constValue; };
Базовый класс: C++ (Qt) class NSimpleDataTypes : public INSimpleDataTypes { public: NSimpleDataTypes() {_id = -1; _name = ""; _constValue = false;} 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 = "";} bool constValue() const {return _constValue;} void setConstValue(bool constValue) {_constValue = constValue;} };
Производный от него класс NNumeric: 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); } QString valueToQString() const { int decpt, sign; QString s(fcvt(_value, 18, &decpt, &sign)); if (decpt > 0 && _value < 0.0 ? _value - ceil(_value) : _value - floor(_value) == 0.0) {//здесь число без цифр отличных от нуля после десятичной запятой s.remove(decpt, s.length() - decpt); } else {//наоборот if (decpt > 0) s = s.insert(decpt,'.'); else { QString s2 = "0."; for (int i = 0; i < decpt * (-1); i++) s2 += '0'; s.insert(0, s2); } bool isNumberNull = true; while(isNumberNull) { if (s[s.length() - 1] == '0') { s.remove(s.length() - 1, 1); isNumberNull = true; } else { isNumberNull = false; } } } //после преобразования числа в строку, проверяем его на отрицательность if (sign != 0) s = s.insert(0, '-'); return s; } //целочисленное деление 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); } bool operator==(NNumeric b) { return this->value() == double(b); } bool operator!=(NNumeric b) { return this->value() != double(b); } bool operator<=(NNumeric b) { return this->value() <= double(b); } bool operator>=(NNumeric b) { return this->value() >= double(b); } bool operator<(NNumeric b) { return this->value() < double(b); } bool operator>(NNumeric b) { return this->value() > double(b); } operator QString() {return valueToQString();} operator double() {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;} double 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 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 checkingFloatBorders(_value);} double _value; bool _isTypeInteger; };
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Eten от Март 27, 2011, 11:25
В общем, ничего такого не нашел. Поэтому сделал, с помощью перечисления. Все работает. Интерфейсы: C++ (Qt) enum NGroupDataTypes { eNSimpleDataTypes }; class INDataTypes { public: virtual ~INDataTypes() { } virtual qint32 id() const = 0; virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0; virtual NGroupDataTypes groupDataTypes() const = 0; protected: //если идентификатор равен -1, значит это либо временная переменная, либо константное значение qint32 _id; //константные или временные значения имен не имеют, только переменные. QString _name; //указание на принадлежность к группе типов данных. NGroupDataTypes _groupdatatypes; }; class INSimpleDataTypes : public INDataTypes { public: virtual QString valueToQString() const = 0; protected: //это указывает на константное значение bool _constValue; };
Базовый класс: C++ (Qt) class NSimpleDataTypes : public INSimpleDataTypes { public: NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; _groupdatatypes = eNSimpleDataTypes; } 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 = "";} NGroupDataTypes groupDataTypes() const {return _groupdatatypes;} bool constValue() const {return _constValue;} void setConstValue(bool constValue) {_constValue = constValue;} };
В производном ничего не менял. И вот так это используется: C++ (Qt) NNumeric numeric= 5; INDataTypes *idt = numeric; bool b = (*idt).groupDataTypes() == eNSimpleDataTypes;
Булев b примет истину. Проблема решена.
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Igors от Март 27, 2011, 12:01
Чем же dynamic_cast не устроил?
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Eten от Март 27, 2011, 12:44
Чем же dynamic_cast не устроил?
А тем, что мне надо проверить, а не пытаться привести в тип до проверки (так быстрее). Да и в программе, с использованием перечисления, код стал понятнее. А все dynamic_cast я выполняю только тогда, когда знаю к чему привести. Да и мне тут ваши коллеги еще в прошлых темах сказали, что использование dynamic_cast в проверке на класс для использования dynamic_cast с привидением на проверяемый класс, работа для не ленивых программистов. Т.е. выполняется дважды. А через перечисление все работает без лишних наворотов и в самой программе оказалось оно нужным, т.е. не лишним. typeid проверяет на конкретные классы сам. Так что dynamic_cast для проверки на тип это уже лишнее, а вот для самого привидения в тип, это как раз то, что нужно. ;)
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Igors от Март 27, 2011, 13:25
Чем же dynamic_cast не устроил?
А тем, что мне надо проверить, а не пытаться привести в тип до проверки (так быстрее). Да и в программе, с использованием перечисления, код стал понятнее. А все dynamic_cast я выполняю только тогда, когда знаю к чему привести. Да и мне тут ваши коллеги еще в прошлых темах сказали, что использование dynamic_cast в проверке на класс для использования dynamic_cast с привидением на проверяемый класс, работа для не ленивых программистов. Т.е. выполняется дважды. А через перечисление все работает без лишних наворотов и в самой программе оказалось оно нужным, т.е. не лишним. typeid проверяет на конкретные классы сам. Так что dynamic_cast для проверки на тип это уже лишнее, а вот для самого привидения в тип, это как раз то, что нужно. ;) А почему просто не сказать "я уже написал и оно (как-то) работает. Переделывать снова я НЕ БУДУ!!" (или просто промолчать). К чему этот анекдот "сначала (самому) проверить а затем уж dynamic_cast" ? :) Разумеется Вы имеете право на собственное мнение и можете принимать даваемые советы или нет. Но сначала надо открыть книжку и прочитать, это недолго и несложно. А потом уж спорить, отстаивать свою точку зрения и.т.д.
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: m_ax от Март 27, 2011, 15:06
Непонял: А тем, что мне надо проверить, а не пытаться привести в тип до проверки (так быстрее). Да и в программе, с использованием перечисления, код стал понятнее. А все dynamic_cast я выполняю только тогда, когда знаю к чему привести.
Если заранее известно к чему нужно привести базовай класс, то dynamic_cast избыточен: как раз для этого предназначен static_cast. И зачем в INDataTypes делать функции C++ (Qt) virtual qint32 id() const = 0; virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0;
виртуальными? Можно вполне сделать так: C++ (Qt) class INDataTypes { public: virtual ~INDataTypes() { } qint32 id() const { return _id; } void setId(qint32 id) { _id = id; } QString name() const { return _name; } void setName(QString name) { _name = name;} virtual NGroupDataTypes groupDataTypes() const = 0; // Оставить только эту фиртуальной protected: //если идентификатор равен -1, значит это либо временная переменная, либо константное значение qint32 _id; //константные или временные значения имен не имеют, только переменные. QString _name; };
Тогда, например в NSimpleDataTypes: C++ (Qt) class NSimpleDataTypes : public INSimpleDataTypes { public: NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; //_groupdatatypes = eNSimpleDataTypes; Нафига лишняя переменная? } NGroupDataTypes groupDataTypes() const {return eNSimpleDataTypes; /*_groupdatatypes; */ } /* Это тоже лишнее */ bool constValue() const {return _constValue;} void setConstValue(bool constValue) {_constValue = constValue;} };
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Igors от Март 27, 2011, 15:41
Если заранее известно к чему нужно привести базовай класс, то dynamic_cast избыточен: как раз для этого предназначен static_cast.
static_cast ничего не проверит, он просто "приведет" если типы позволяют, NULL он не вернет
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: m_ax от Март 27, 2011, 15:55
Если заранее известно к чему нужно привести базовай класс, то dynamic_cast избыточен: как раз для этого предназначен static_cast.
static_cast ничего не проверит, он просто "приведет" если типы позволяют, NULL он не вернет Согласен) В том то всё и дело, что проверка не нужна, если заранее известно к чему приводить) Или мы о разном говорим?
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Eten от Март 27, 2011, 16:06
m_ax, переменная _groupdatatypes вовсе не лишняя, она то мне и помогла разобраться с определением принадлежности класса к группе. А в классе NSimpleDataTypes определяется функция groupDataTypes, которая выдает значение той самой переменной. Т.е. _groupdatatypes инициализуемая в конструкторе указывает на то, что все наследуемые от NSimpleDataTypes производные классы будут принадлежать к eNSimpleDataTypes, с которыми программа работает, через интерфейсы определенным образом. Т.е. для это оказался самый практичный способ решения проблемы и к тому же удобный во многих отношениях, если заглядывать вперед.
Igors , я никого не хотел обижать или с кем-то спорить. Но ваш вопрос показался мне странным (ведь на вопросы стоит отвечать), поэтому мне показалось, что я что-то мог упустить из внимания. ;)
З.Ы. За советы и рекомендации разумеется, огромное спасибо. Код дальше изменять здесь уже не стану мне достаточно и той реализации, к которой пришел. :)
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: m_ax от Март 27, 2011, 16:15
m_ax, переменная _groupdatatypes вовсе не лишняя, она то мне и помогла разобраться с определением принадлежности класса к группе. А в классе NSimpleDataTypes определяется функция groupDataTypes, которая выдает значение той самой переменной. Т.е. _groupdatatypes инициализуемая в конструкторе указывает на то, что все наследуемые от NSimpleDataTypes производные классы будут принадлежать к eNSimpleDataTypes, с которыми программа работает, через интерфейсы определенным образом. Т.е. для это оказался самый практичный способ решения проблемы и к тому же удобный во многих отношениях, если заглядывать вперед.
Всё равно не понял) Чем, например это: C++ (Qt) NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; _groupdatatypes = eNSimpleDataTypes; } NGroupDataTypes groupDataTypes() const {return _groupdatatypes; } Отличается от: C++ (Qt) NSimpleDataTypes() { _id = -1; _name = ""; _constValue = false; } NGroupDataTypes groupDataTypes() const {return eNSimpleDataTypes; }
? И потом, надо помнить, что вызов виртуальной функции обходится дороже, вызова обычной функции и там где есть возможность обойтись без виртуальных функций лучше обходится без них. У вас же как раз такой случай в отношении 3 функций: C++ (Qt) virtual void setId(qint32 id) = 0; virtual QString name() const = 0; virtual void setName(QString name) = 0; Какой по вашему смысл в том, чтобы они были виртуальными? Чтобы в наследуемых классах каждый раз переопределять их одинаковым образом увеличивая при этом число строк кода и снижая производительность при их вызове?
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Igors от Март 27, 2011, 16:31
Igors , я никого не хотел обижать или с кем-то спорить. Но ваш вопрос показался мне странным (ведь на вопросы стоит отвечать), поэтому мне показалось, что я что-то мог упустить из внимания. ;)
Я и не обижаюсь :) Код дальше изменять здесь уже не стану мне достаточно и той реализации, к которой пришел. :)
Это совершенно нормально, никто и не думает настаивать, ведь проект Ваш Единственная причина по которой я пишу - это сентенции типа таких ...Т.е. для это оказался самый практичный способ решения проблемы и к тому же удобный во многих отношениях, если заглядывать вперед.
Никто не осуждает Вас за недостаток опыта в ++, но зачем прикрывать это словами "самый практичный", "удобный во многих.."? :) Ваш способ давно известен и квалифицируется библией как "издевательство над принципами ООП"
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: Eten от Март 27, 2011, 17:49
Спасибо, m_ax, все никак не отвыкну от привычек C#-а.
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: alexcpp от Апрель 05, 2011, 00:11
http://www.boost.org/doc/libs/1_46_1/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html иникакова рунтима. все в компилетиме. никакова дапалнительнага разхода CPU.
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: m_ax от Апрель 05, 2011, 00:29
http://www.boost.org/doc/libs/1_46_1/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html иникакова рунтима. все в компилетиме. никакова дапалнительнага разхода CPU.
Всё же здесь немного другой случай. Здесь и так известно к чему приводить. И приводится без всякого рантайма) Кстати, type_traits: можно юзать в c++0x #include <tr1/type_traits> ;)
Название: Re: Полиморфизм, наследование, проверка на принадлежность к группе классов.
Отправлено: alexcpp от Апрель 05, 2011, 14:40
#include <type_traits>
у вас наверна очень давний компелатор.
|