Главная Случайная страница


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать неотразимый комплимент Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?

Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Доступ к компонентам структурированного объекта





Язык Си++ поддерживает одну из концепций объектно-ориентированного программирования, так называемую инкапсуляцию (сокрытие) данных. Она заключается в том, что внутренняя реализация (компонентные данные) не должны быть доступны за пределами определений тел компонентных функций класса. Если же возникает необходимость всё же получить доступ или значение компонентных данным, то это делается за счёт общедоступных (интерфейсных) компонентных функций. Другими словами, в правильно сконструированном классе должна присутствовать скрытая часть, которая, безусловно, содержит все компонентные данные и может содержать некоторое количество компонентных функций, и открытая (общедоступная, интерфейсная) часть, представленная набором компонентных функций, которые могут быть вызваны в любом месте программы. Предполагается, что скрытая часть может меняться, модифицироваться и т.д., а интерфейсная часть остаётся неизменной (т.е. остаются неизменными сигнатуры интерфейсных компонентных функций, следовательно, остаются неизменными и способ вызова этих функций во всей остальной программе, но определение безусловно может меняться ).

Таким образом, при использовании принципа инкапсуляции при программировании программу можно мысленно разбить на две достаточно независимые части. Одна часть это объект класса, который имеет внутреннюю (скрытую) часть и некоторый набор интерфейсных функций, а другая часть это вся остальная программа, которая взаимодействует с объектом по средствам вызова интерфейсных функций, и если в результате любых модификаций скрытой части объекта сигнатура этих интерфейсных функций не меняется, то во всей остальной программе нет необходимости, что-либо менять, а значит нет необходимости заново её отлаживать, анализировать, находить ошибки и т.д.. Поэтому инкапсуляция в больших программах экономит очень много сил и времени разработчикам.

В языке Си++ инкапсуляция поддерживается с помощью трёх спецификаторов доступа (ключевых слов) private (частный ), protected (защищённый ), public (общедоступный ). В определении класса компонентные данные и компонентные функции, стоящие после любого из ключевых слов и до конца определения класса или до другого спецификатора доступа считаются частными, защищёнными или общедоступными соответственно. Если при определении класса используется ключевое слово struct, то по умолчанию считается, что компонентные данные являются общедоступными, если class, то частными. Это принятое по умолчанию положение можно изменить явно указав спецификатор доступа. Например:



class F { //поскольку использовано ключевое слово class, то по умолчанию все компонентные //данные и функции размещённые в определении c начала определения и до нового //спецификатора доступа, а если он отсутствует, то до конца определения класса будут //частными (private)

компонентные данные; //private данные

компонентные функции; //private компонентные функции

public: //C этого точки определения и до следующего спецификатора доступа или, если он //отсутствует, до конца определения класса все определённые компонентные данные и //компонентные функции будут считаться общедоступными

компонентные данные; //public данные

компонентные функции; //public компонентные функции

};

Перепишем класс комплексное число с учётом принципа инкапсуляции. Определим все его компонентные данные как частные ( private ), а компонентные функции как общедоступные (public).

class comp { //Скрытая (частная) часть класса comp

double Re, Im;

static int count;

public: //Интерфейсная (общедоступная) часть класса comp

comp (){Re= 0; Im= 0; count++;} //Компонентная функция (конструктора по умолчанию)

comp(double r, double i) {Re = r; Im = i; count++;} //Конструктор

comp(comp &T) { Re= T.Re; Im = T.Im; count++;} // Конструктор копии

comp(double r) {Re = r; Im=0; count++;} // Конструктор преобразования типа

~comp(){count--;} // Деструктор

void display(){cout<<“\n Re =”<<Re<<”\t Im =“<<Im;}//Компонентная функции display()

static int GetCountComp(){return count;}

void SetRe(double r){Re = r;}

void SetIm(double i){ Im = i;}

double GetRe(){return Re;}

double GetIm(){return Im;}

friend comp raz(comp& A, comp& B);

};

int comp::count = 0; //Инициализация статического компонентного данного

Поскольку теперь компонентные данные Re, Im и count класса сomp являются частными, то к ним с помощью операции точка или стрелочка теперь можно обратится только в теле компонентных функций класса comp. Например, в конструкторе копии мы видим такое обращение Re= T.Re; Во всех других функциях, в том числе и main(), эта строка выдавала бы на этапе компиляции сообщение об ошибке. Поэтому, если все таки возникает необходимость изменять или получать значения реальной и мнимой части объекта комплексное число вне тел компонентных функций класса comp, то необходимо написать несколько интерфейсных функций SetRe(), SetIm(), GetRe(), GetIm(), которые и решают эту задачу. Причём поскольку тела этих функций очень маленькие, то мы делаем их online (подставляемыми), поэтому вызов такой функции не сильно отличается от прямого обращения к полю данных структурированного объекта, следовательно, почти не замедляет скорость работы программы.



Перепишем определение функции sum () с учётом того, что доступ к полям Re и Im теперь можно получить только через вызов компонентных функций SetRe(), SetIm(), GetRe(), GetIm().

Определение обычной (не компонентной) функции sum()

comp sum(comp A, comp& С){

comp D;

D.SetRe( A.GetRe() +С.GetRe());

D.SetIm ( A.GetIm() +С.GetIm());

return D;

}

Как видно из определения функции sum (), оно стало несколько громоздким. Этого можно избежать воспользовавшись ещё одной возможностью, предоставляемой языком Си++. Она состоит в том, что можно объявить функцию, дружественной к классу. В этом случае дружественная функция будет иметь право доступа к любым данным класса в не зависимости от их спецификатора доступа. Для того чтобы сделать функцию дружественной необходимо, чтобы она была описана в определении класса со спецификатором friend, как показано выше в определении класса на примере функции raz().

Определение обычной (не компонентной) функции raz()

comp raz(comp& A, comp& С){

comp D;

D.Re = A.Re -С.Re;

D.Im = A.Im -С.Im;

return D;

}

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

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

Итак, дружественная функция:

• не может быть компонентной функцией того класса, по отношению к которому определяется как дружественная;

• может быть глобальной функцией (как в предыдущей программе):

class CL { friend int f1 (...); ... }; int fl(...) { тело_функции }

• может быть компонентной функцией другого ранее определенного класса:

class CL1 { ... char f2 (...); ... };

class CL2 { ... friend char CL1::f2(...); ... } ;

В примере класс CL1 c помощью своей компонентной функции f2 () получает доступ к компонентам класса CL2. Компонентная функция некоторого класса (CL1) может быть объявлена дружественной функцией другому классу (CL2), если только определение этого первого класса размещено раньше, чем определение второго (CL).

• может быть дружественной по отношению к нескольким классам:

// Предварительное неполное определение класса class CL2;

class CL1 { friend void ff{CLl,CL2); ... };

class CL2 { friend void ff{CLl,Cl2); ... };

void ff(CL1 cl,CL2 c2) { тело_функции }

Кроме того, класс может быть дружественным другому классу. Это означает, что все компонентные функции класса являются дружественными для другого класса. Дружественный класс должен быть определен вне тела класса, "предоставляющего дружбу". Например, так:

class X2 {

friend class X1;

};

class X1 { ... // Определение дружественного класса

void f1(...);

void f2(...);

};

 

В данном примере функции f1 и f2 из класса X1 являются друзьями класса Х2, хотя они описываются без спецификатора friend.

Все компоненты класса X2 доступны в дружественном классе X1. Дружественный класс может быть определен позже (ниже), нежели описан как дружественный.

 






Date: 2015-05-22; view: 356; Нарушение авторских прав

mydocx.ru - 2015-2019 year. (0.007 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию