У меня возникли сомнения: в своем проекте, который, должен возвращать по текущей позиции указателя на экране элемент, написал следующий код:
enum EL_TYPES
{
EL_NULL, EL_TEXT, EL_LABEL, EL_EDIT, EL_LIST
};
class CElement
{
public:
CElement( void );
void init( void );
EL_TYPES type( void ){ return el_type; };
. . .
int x;
int y;
int width;
. . .
protected:
EL_TYPES el_type;
char* el_data;
};
class CScreen
{
public:
CScreen( void ){ init(); };
void init( void );
. . .
CElement& get_element( int x, int y )
{
//if ( x + length > owner->width() ) return false;for ( int i = 0; i < count(); i++ )
{
if ( elements[i]->y == y && elements[i]->x <= x && elements[i]->x + elements[i]->width > x ) return *elements[i];
}
return *((CElement*)NULL);
}
CElement& operator [] ( int index )
{
if ( index == -1 ) return *((CElement*)NULL);
else return *elements[index];
};
. . .
protected:
char name[64];
std::vector<CElement*> elements;
};
????????????????????????
Вопрос сводится к вызову метода get_element и оператора [] в классе CScreen. Красиво ли будет выглядеть код:
????????????????????????
CScreen& s = get_current_screen();
CElement& e = s.get_element( m_caret_pos.x, m_caret_pos.y );
if ( &e && e.type() == EL_TEXT )
{
. . .
}
или все-таки вместо ссылке здесь красивее было бы использовать указатель:
CElement* get_element( int x, int y )
соответственно:
CElement* e = s.get_element( m_caret_pos.x, m_caret_pos.y );
if ( e && e->type() == EL_TEXT )
!!!!!!!!!!!!!!!!!!!!!!!!
Почему я использовал ссылку – потому, что можно перегрузить оператор EL_TYPES:
!!!!!!!!!!!!!!!!!!!!!!!!
operator EL_TYPES ( void ) { return type(); };
и, тогда:
if ( &e && e == EL_TEXT )
{
. . .
}
, что мне кажется – очень удобным.
Спасибо за внимание. Критикуйте. (-:
Не забывайте, пожалуйста, использовать теги [c]...[/c], [code]...[/code] и т.п. для выделения фрагментов кода. -- ПК
Re: Корректное и удобное использование указателей и ссылок.
Здравствуйте, Andreev, Вы писали:
A>У меня возникли сомнения: в своем проекте, который, должен возвращать по текущей позиции указателя на экране элемент, написал следующий код:
...
A> CElement& get_element( int x, int y )
A> {
A> //if ( x + length > owner->width() ) return false;
A> for ( int i = 0; i < count(); i++ )
A> {
A> if ( elements[i]->y == y && elements[i]->x <= x && elements[i]->x + elements[i]->width > x ) return *elements[i];
A> }
A> return *((CElement*)NULL);A> // Руки обрывать!
}
A> CElement& operator [] ( int index )
A> {
A> if ( index == -1 ) return*((CElement*)NULL);// Аналогично
A> else return *elements[index];
A> };
A> std::vector<CElement*> elements; // Опасно, но не критично
A>};
A>???????????????????????? A>Вопрос сводится к вызову метода get_element и оператора [] в классе CScreen. Красиво ли будет выглядеть код: A>????????????????????????
A>CScreen& s = get_current_screen();
A>CElement& e = s.get_element( m_caret_pos.x, m_caret_pos.y );
A>if ( &e && e.type() == EL_TEXT ) // Coding Guidelines: Never Switch on Type
A>{
A> . . .
A>}
A>Спасибо за внимание. A> Критикуйте. (-:
Посмотреть, какие методы можно объявить константными
Возвращать, по возможности, константные ссылки
Пересмотреть архитектуру с целью избавления от if(EL_xxx == e.type())
В циклах лучше использовать итераторы
И лучше использовать умные указатели
HgLab: Mercurial Server and Repository Management for Windows
Re: Корректное и удобное использование указателей и ссылок.
Здравствуйте, Andreev, Вы писали:
A>У меня возникли сомнения: в своем проекте, который, должен возвращать по текущей позиции указателя на экране элемент, написал следующий код:
A>enum EL_TYPES
A>{
A> EL_NULL, EL_TEXT, EL_LABEL, EL_EDIT, EL_LIST
A>};
Антипаттерн «Код типа». Возможные последствия: трудноподдерживаемые длинные switch (code) { }. Лечение: рефакторинг Replace Type Code with Subclasses [1].
class Element
{
public:
virtual ~Element() = 0;
};
Element::~Element() {}
class NullElement : public Element
{
public:
~NullElement()
{}
};
class TextElement : public Element // …
// …
A> CElement& get_element( int x, int y )
A> {
A> for ( int i = 0; i < count(); i++ )
A> {
A> if ( elements[i]->y == y &&
elements[i]->x <= x &&
elements[i]->x + elements[i]->width > x )
{
return *elements[i];
}
A> }
A> return *((CElement*)NULL);
A>}
Ссылка не имеет права быть нулевой. Верни, например, new NullElement [2].
A>Вопрос сводится к вызову метода get_element и оператора [] в классе CScreen. Красиво ли будет выглядеть код:
A>CScreen& s = get_current_screen();
A>CElement& e = s.get_element( m_caret_pos.x, m_caret_pos.y );
A>if ( &e && e.type() == EL_TEXT )
A>{
A> . . .
A>}
A>или все-таки вместо ссылке здесь красивее было бы использовать указатель:
A> CElement* get_element( int x, int y )
A>соответственно:
A>CElement* e = s.get_element( m_caret_pos.x, m_caret_pos.y );
A>if ( e && e->type() == EL_TEXT )
а уже doSomething реализован в NullElement как {}, в TextElement — как надо, в LabelElement — как-то ещё. Если таких блоков много, то можно использовать паттерн Visitor [3]:
class ElementVisitor;
class Element
{
public:
virtual void accept(ElementVisitor&) = 0;
};
class NullElement : public Element
{
public:
void accept(ElementVisitor&);
};
class TextElement : public Element
{
public:
void accept(ElementVisitor&);
};
//…class ElementVisitor
{
public:
virtual void visit(NullElement&) = 0;
virtual void visit(TextElement&) = 0;
//…
};
void NullElement::accept(ElementVisitor& v)
{
v.visit(*this);
}
void TextElement::accept(ElementVisitor& v)
{
v.visit(*this);
}
//…
// где-то далекоclass DoSomethingWithTextElement : public ElementVisitor
{
public:
void visit(NullElement&) {}
void visit(TextElement& te)
{
// do something with te
}
//…
};
s.get_element(pos).accept(DoSomethingWithTextElement());
A>Почему я использовал ссылку – потому, что можно перегрузить оператор EL_TYPES:
A> operator EL_TYPES ( void ) { return type(); };
A>и, тогда:
A>if ( &e && e == EL_TEXT )
A>{
A> . . .
A>}
A>, что мне кажется – очень удобным.
А мне — опасным. Неявные преобразования типов вообще нужно хорошо обдумывать.
Ссылки на литературу:
[1] Martin Fowler, Kent Beck et al. Refactoring: Improving the Design of Existing Code. p. 181.
[2] Jim Hyslop, Herb Sutter. Null References. // C++ Report, 12(6), June 2000.
[3] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. p. 331.
Re[2]: Корректное и удобное использование указателей и ссыло
Изначально строились 3-и класса CPanel, CScreen и CElement. Все эти классы существуют как некий документ и не более.
Панель — это класс некоторой панели(физическое устройство), которая в свою очередь содержит набор экранов(логические единицы панели), а экраны содержат элементы(логические единицы экрана).
Задача заключается в том, чтобы собрать редактор для добавления экранов в панель и расположения элементов внутри экранов, задавая им некоторые свойства в зависимости от типов. В конечном итоге, при помощи методов store() выгрузить все эти данные в программу управления реальной панелью, которая позволит получить на панели логическую систему меню, сообщений и форм ввода/вывода.
Программа-транслятор, для реальной панели, так-же написана на c++, за исключением платформы(в настоящий момент используется DOS).
По-этому не хотелось делать несколько реализаций классов CPanel, CScreen и CElement для каждой платформы, а сделать всего-лишь один вариант с методами load() и store(). После чего, использовать эти классы в зависимости от того в какой программе они используются (редактор или транслятор), как некое хранилище данных.
Если использовать механизм наследования и создавать потомков класса CElement с готовыми методами для использования их в редакторе (т.е. некие методы DoSomething()), то в результате окажется, что при повторном использовании кода этого класса в трансляторе у меня появится неиспользуемый код, т.к. в трансляторе свойства элементов не меняются и остаются постоянными.
Кроме того — если отказаться от задания типов элементам, то транслятору будет достаточно тяжело определить какого типа элемент он обрабатывает.
Можно, конечно, унаследовать класс CPanel и получить что-то типа CEditablePanel и CTranslatedPanel, но все равно методы редактирования и трансляции будут разными (я не вижу необходимости создавать над всем этим класс), а структура документа (набор свойств элементов) постоянна. И необходим класс для элемента, который мог бы сообщить где он находится на экране, какого он типа и какие данные содержит — больше ничего.
Я пришел к выводу, ест-но, посовещавшись с умными люми, что наиболее подходящим решением будет возвращать индексы эл-тов, а не указатели или ссылки(при этом рассматривался вариант использования итереаторв, но в данном случае он громоздок).
Я, так-же, полностью согласен с высказываниями по поводу возврата пустых ссылок (про руки). Это уже на самом деле был плод больной фантазии невыспавшегося организма. Хотя должен признать, что изначально идея null-referenses мне очень понравилась (хотя это всего-лишь мое мнение, которое не совпадает с концепцией ссылок в с++), но я во благо Человечества решил от нее отказаться .
Очень благодарен за исчерпывающий ответ, мне, как человеку, недавно использующему с++, он дал неоценимую помощь. Ссылки на литературу — это по моему было сверх всяких ожиданий. И я честно обещаю проработать этот вопрос.
Удачи.
Re[3]: Корректное и удобное использование указателей и ссыло
Здравствуйте, Andreev, Вы писали:
A>Для этого кода еще существует одна проблема:
A>Изначально строились три класса CPanel, CScreen и CElement. Все эти классы существуют как некий документ и не более. A> Панель — это класс некоторой панели(физическое устройство), которая в свою очередь содержит набор экранов(логические единицы панели), а экраны содержат элементы(логические единицы экрана).
A> Задача заключается в том, чтобы собрать редактор для добавления экранов в панель и расположения элементов внутри экранов, задавая им некоторые свойства в зависимости от типов. В конечном итоге, при помощи методов store() выгрузить все эти данные в программу управления реальной панелью, которая позволит получить на панели логическую систему меню, сообщений и форм ввода/вывода. A> Программа-транслятор, для реальной панели, так-же написана на c++, за исключением платформы(в настоящий момент используется DOS).
А что, под DOS таки существуют компиляторы C++?
A> Поэтому не хотелось делать несколько реализаций классов CPanel, CScreen и CElement для каждой платформы, а сделать всего-лишь один вариант с методами load() и store(). После чего, использовать эти классы в зависимости от того в какой программе они используются (редактор или транслятор), как некое хранилище данных.
Я так понимаю, в редакторе у этих классов одно поведение, в трансляторе другое? И набор методов разный. И всё разное, кроме представления данных. Так зачем хотеть реализовать оба проекта на одних и тех же классах?
A> Если использовать механизм наследования и создавать потомков класса CElement с готовыми методами для использования их в редакторе (т.е. некие методы DoSomething()), то в результате окажется, что при повторном использовании кода этого класса в трансляторе у меня появится неиспользуемый код, т.к. в трансляторе свойства элементов не меняются и остаются постоянными.
Любой мало-мальски уважающий себя компилятор выбросит неиспользуемый код и не будет включать его в финальный бинарник.
A> Кроме того — если отказаться от задания типов элементам, то транслятору будет достаточно тяжело определить какого типа элемент он обрабатывает.
Ему не нужно определять, ему нужно подсунуть элементу соответствующим образом реализованного visitor’а.
Re[4]: Корректное и удобное использование указателей и ссыло
Centaur wrote:
> A> Задача заключается в том, чтобы собрать редактор для добавления > экранов в панель и расположения элементов внутри экранов, задавая им > некоторые свойства в зависимости от типов. В конечном итоге, при > помощи методов store() выгрузить все эти данные в программу управления > реальной панелью, которая позволит получить на панели логическую > систему меню, сообщений и форм ввода/вывода. > A> Программа-транслятор, для реальной панели, так-же написана на c++, > за исключением платформы(в настоящий момент используется DOS). > А что, под DOS таки существуют компиляторы C++?
DigitalMars, GCC, Watcom.
--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[4]: Корректное и удобное использование указателей и ссыло
Здравствуйте, Centaur, Вы писали:
C>А что, под DOS таки существуют компиляторы C++?
Я использую BC++ 5.0. Поскольку у меня контроллер на 386SX, то получается достаточно удобно и оптимизация поддерживается где-то до 150-го Пентиума. Не надо писать неудобный код на ассемблере (в смысле основные блоки у меня описаны классами). Для самих-же модулей ввода-вывода (используется кристал Atmel 8015) приходится писать на asm-е, хотя там и есть подобие C, но у меня нет компилятора (лень скачать).
C>Я так понимаю, в редакторе у этих классов одно поведение, в трансляторе другое? И набор методов разный. И всё разное, кроме представления данных. Так зачем хотеть реализовать оба проекта на одних и тех же классах?
Идея в том, что эти классы являются контейнерами для данных. Они должны выдавать описание свойств тех или инных объектов. Поведение-же этих объектов отличается, по-этому, методы реализованы на основе других классов. Единственными методами могут быть методы проверки корректности данных в этом классе (по идее, конечно).
C>Любой мало-мальски уважающий себя компилятор выбросит неиспользуемый код и не будет включать его в финальный бинарник.
Я согласен, но во-первых не уверен с данным утверждением для BC++ 5.0, а во-вторых, как правило, если ф-ция виртуальная, то она всеравно остается, поскольку очень тяжело на 100% определить, что не будет вызова этой ф-ции.
C>Ему не нужно определять, ему нужно подсунуть элементу соответствующим образом реализованного visitor’а.
Здесь я согласен, но нужно продумать механизм, какого визитора подсовывать. И если все это передается через некий поток (файл), то в нем всеравно надо сохранять типы элементов (может я конечно о чем-то не подозреваю). По этим типам, в принципе, конечно можно создать динамически создаваемые подклассы, хотя механизм получается достаточно сложный (скорее всего неоправданно).