Функции-элементы, дружественные функции, константные функции
Поля данных, исходя из принципа скрытия данных, всегда должны быть защищены от несанкционированного доступа. Доступ к ним, как правило, должен осуществляться только через функции, включающие методы чтения и записи полей. В этих функциях должна осуществляться проверка данных, чтобы не записать случайно в поля неверные данные или чтобы не допустить их неверной трактовки. Кроме того, функции чтения позволяют не переписывать использующую их программу, даже если необходимо изменить что-то в типе, способах хранения и размещения данных в классе.
Поэтому данные всегда целесообразно объявлять в разделе private— закрытом разделе класса. В редких случаях их можно помещать в protected— защищенном разделе класса, чтобы возможные потомки данного класса имели к ним доступ.
Приведем пример. Пусть класс имеет следующее объявление:
class MyClass
{
public:
void SetA(int); // функция записи
int GetA(void); // функция чтения
private:
int FA;
double В, С;
};
Реализация функций записи и чтения может иметь вид:
void MyClass::SetA(int Value){
if (...) // проверка корректности данных
FA = Value;
}
int MyClass::GetA(void) {return FA;}
*** для оценки if((Table1OZ->Value<=5)&&(Table1OZ->Value>=0))
В данном случае функция чтения просто возвращает значение поля, но в более сложных классах может потребоваться какая-то предварительная обработка данных. Все описания функций-элементов определяемых вне класса содержат ссылку на класс с помощью операции разрешения области действия "::".
В приведенном примере объявление класса содержит только прототипы функций, а их реализация вынесена из описания класса. Для простых функций реализация может быть размещена непосредственно в объявлении класса. Например:
class MyClass
{
public:
void SetA(int Value) {FA= Value;}; // функция записи
int GetA(void) {return FA;} // функция чтения
private:
int FA;
double В, С;
};
Функции, описание которых содержится непосредственно в объявлении класса, в являются встраиваемыми функциями inline.
Введение описания функций в объявление класса — является плохим стилем программирования: следует избегать смешения открытого интерфейса класса, содержащегося в его объявлении, и реализации класса. Если необходимо реализовать встраиваемые функции, то лучше поместить в объявлении класса их прототип со спецификатором inline:
inline void SetA(int); // функция записи
и отдельно дать реализацию функции. При этом в реализации спецификатор inline не указывается.
Объявления классов следует размещать в заголовочном файле модуля (*.h), а реализацию функций — элементов в отдельном файле реализации (*.cpp). При этом в объявлении класса должны содержаться только прототипы функций. Это следует из принципа скрытия информации — одного из основных в объектно-ориентированном программировании. Такая организация программы обеспечивает независимость всех модулей, использующих заголовочный файл с объявлением класса, от каких-то изменений в реализации функций-элементов класса.
Функции-элементы класса имеют доступ к любым другим функциям-элементам и к любым данным-элементам, как открытым, так и закрытым. Клиенты класса (какие-то внешние функции, работающие с объектами данного класса) имеют доступ только к открытым функциям-элементам и данным-элементам. Но в некоторых случаях желательно обеспечить доступ к закрытым элементам для функций, не являющихся элементами данного класса. Это можно сделать, объявив соответствующую функцию как друга класса с помощью спецификации friend. Например, если в объявление класса включить оператор
friend void IncFA (MyClass *);
то функция IncFA, не являясь элементом данного класса, получает доступ к его закрытым элементам. Например, функция IncFA может быть описана где-то в программе следующим образом:
void IncFA(MyClass *P) {P->FA++;}
Дружественными могут быть не только функции, но и целые классы. Например, можно поместить в объявление своего класса оператор
friend Classl;
и все функции-элементы класса Classl получат доступ к закрытым элементам этого класса.
Иногда у программиста может возникнуть необходимость создать объект класса как константный с помощью спецификатора const. Например:
const Classl MC1(3);
Если при этом класс содержит не только функции чтения, но и записи данных, то реакция на такой оператор, введенный пользователем, зависит от версии и настройки компилятора. Компилятор может выдать сообщение об ошибке и отказаться от компиляции, а может просто выдать предупреждение и проигнорировать спецификатор пользователя const. Если класс содержит только функции чтения, то все должно быть нормально. Но компилятор все равно выдаст предупреждение, а может отказаться компилировать программу.
Чтобы избежать этого, можно объявить функции чтения как константные. Для этого и в прототипе, и в реализации после закрывающей список параметров круглой скобки надо написать спецификатор const. Например, можно включить в объявление класса оператор
int GetA(void) const;
а реализацию этой функции оформить как:
int MyClass::GetA(void) const {return FA;}
Тогда замечания компилятора о константных объектах исчезнут.
Таким образом, если предполагается, что объект класса может быть объявлен константным, необходимо снабжать все функции-элементы класса, предназначенные для чтения данных, спецификаторами const.