Re: Корректное и удобное использование указателей и ссылок.
От: Centaur Россия  
Дата: 19.07.05 14:43
Оценка: 39 (2)
Здравствуйте, 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 )


Красивее — будет такое:

s.get_element(caretPos).doSomething(может, быть, ещё, какие-нибудь, параметры);


а уже 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.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.