Правила программирования на Си и Си++

       

Не размещайте тела функций в определениях классов


Здесь есть несколько проблем. Если вы действительно поместите тело функции в определение класса таким образом:

class amanda

{

public:

   void

peekaboo( void ){ cout "ку-ку\n"; }

// функция игры

                                               // в прятки с

                                               // Амандой

}

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

class amanda

{



public:

   void peekaboo( void );

}

class amanda::peekaboo( void )

{

   cout "ку-ку\n";

}

Путаница —

более крупная проблема, чем размер. Часто определение класса является единственной имеющейся у вас определенной документацией по членам класса. Вам на самом деле нужно, чтобы все поместилось на одной странице, и чтобы это определение давало краткий список прототипов функций. Если имена функции и аргумента выбраны точно, то это часто вся документация, которая вам необходима.

Как только вы начинаете добавлять тела функций, даже если они состоят из одной строки, к определению класса - вы эту ясность теряете. Определение класса начинает распространяться на несколько страниц, и становится трудно найти что-нибудь, используя определение класса в качестве средства документирования.

Третья проблема более коварна и потребует нескольких часов на устранение, если вы не будете аккуратны. Рассмотрим фрагмент реализации связанного списка на листинге 8 (который не будет компилироваться). Классы linked_list и list_node

посылают сообщения друг другу. Компилятор должен увидеть определение класса до того, как он позволит вам послать сообщение объекту этого класса. (Вы можете объявить указатель на объект, лишь глядя на class xxx; но вы не можете ничего сделать при помощи этого указателя до завершения определения всего класса). Так как в листинге 8 используются встроенные функции, то невозможно устроить эти определения классов так, чтобы избежать предварительных ссылок. Вы можете решить эту проблему, поместив определения функций в конце того файла, где они объявлены. Я сделал это в листинге 9.




Листинг 8. Фрагмент реализации связанного списка
class list_node;
class linked_list
{
    int number_of_elements_in_list;
    list_node *root;
private:     // этот раздел содержит сообщения, получаемые
    friend class list_node; // только от объектов list_node
    void have_removed_an_element(void)
    {
       --number_of_elements_in_list;
    }

public:
    void remove_this_node( list_node *p )
    {
    // Следующая строка генерирует ошибку при компиляции,
    // так как компилятор не знает, что list_node
    // имеет сообщение remove_yourself_from_me( root ).

       p-remove_yourself_from_me( root );
    }

// ...
};

class list_node
{
    linked_list *owner;
private:                     // Этот раздел содержит
    friend class linked_list; // сообщения,получаемые только
                              // от объектов linked_list
    void remove_yourself_from_me( list_node *root )
    {
    // ... Выполнить удаление
       owner-have_removed_an_element();
    }
};
Листинг 9. Улучшенный вариант реализации связанного списка
class list_node;
class linked_list
{
    int number_of_elements_in_list;
    list_node *root;
private:
    friend class list_node;
    void have_removed_an_element( void );

public:
    void remove_this_node( list_node *p );

//...
};
//========================================================
class list_node
{
    linked_list *owner;
private:                // Этот раздел содержит сообщения,
    friend class linked_list; // получаемые только от
                              // объектов linked_list

    void remove_yourself_from_me( list_node *root );
};

//========================================================
// функции класса linked_list:
//========================================================
inline void
linked_list::remove_this_node( list_node *p )
{
    p-remove_yourself_from_me( root );
}
//--------------------------------------------------------
inline void
linked_list::have_removed_an_element( void )
{
    --number_of_elements_in_list;
}

//========================================================
// функции класса list_node:
//========================================================
void list_node::remove_yourself_from_me( list_node *root )
{
// ... Выполнить удаление
    owner-have_removed_an_element();
}

Содержание раздела