Приватные или защитные члены класса 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Приватные или защитные члены класса



Методология ООП

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

Основное требование к модели программы – она должна быть понятна заказчику программы, пользователю и группе программистов – разработчиков.

Объектно-ориентированное программирование – совокупность технологий, принципов, инструментальных средств, для создания программных систем на основе архитектуры взаимодействия объектов.

Основные принципы ООП:

1) Абстракция

2) Инкапсуляция

3) Наследование

4) Полиморфизм (многообразие)

 

Пример описания модели:

Class Box

{

Public:

Double H;

Double L;

Double W;

Double volume ()

{return H*L*W;}

};

 

Абстракция

– характеристика сущности, которая отличает ее от других сущностей. Абстракция определяет границу представления соответствующего объекта модели и применяется для определения таких фундаментальных понятий ООП как класс или объект.

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

Класс представляет собой абстракцию совокупности реальных объектов, которые имеют общий набор свойств и обладают одинаковым поведением.

Объект (в контексте ООП) рассматривается как экземпляр соответствующего класса.

Объекты, которые не имеют идентичных свойств или не обладают одинаковым поведением, по определению не могут быть отнесены к одному классу.

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

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

Пример обозначений класса и связи между базовым и производным классом на языке UML. Стрелка указывает на базовый класс от производного.

Инкапсуляция

- Характеризует сокрытие отдельных деталей внутреннего устройства классов от внешних по отношению к нему объектов или пользователей. Инкапсуляция ведет свое происхождение от деления модулей, в некоторых языках программирования, на 2 части: интерфейс и реализацию. При этом в интерфейсной части модуля описываются все объявления, функций и процедур, а возможно и типов данных, доступных за пределами модуля. Указанные процедуры и функции являются способами оказания услуг внешним клиентам. В секции реализации содержится программный код, определяющий конкретные способы реализации объявленных в интерфейсной части процедур и функций. Объявление функции называется прототипом функции.

 

Полиморфизм

- понимается свойство объектов принимать различные внешние формы, в зависимости от обстоятельств. Применительно к ООП, полиморфизм означает, что действия, выполняемые одноименными методами, могут отличаться, в зависимости от того, к какому классу относится тот или иной метод.

Полиморфизм объектно-ориентированных языков связан с перегрузкой функции, но не тождественен ей. Важно понять, что имена методов и свойств тесно связаны с классами, в которых они описаны.

Полиморфизм тесно связан с механизмом работы виртуальных функции.

Double Sum (double a, double b)

{return a+b;}

Int Sum (int a, int b)

{return a+b;}

Double Sum (double* a, double b)

{double s=0.0;

For (int i=0; i<n; i++)

{s+=a[i]; return s;}

Double Sum (double a[10])

{double s=0.0;

For (int i=0; i<10; i++)

{s+=a[i];

return s;}

 

Нужная функция будет определена по значениям переменных.

Типы возвращаемого значения не относятся к типу функции.

Int main()

{ double* a=new double[10];

Sum (a,10);

Sum(a[10]);

Sum(10,10);

Sum(10.0,10.0);

}

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

Определение собственных типов данных

Структура и класс.

Структура – собственный или пользовательский тип данных, определяемы с помощью ключевого слова “struct”.

Класс – тоже пользовательский или абстрактный тип данных, определяемый с помощью ключевого слова “class”.

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

Struct BOOK

{

Char title[80];

Char author[80];

Int year;

};

Class BOOK

{

Public:

Char title[80];

Char author[80];

Int year;

};

“public” является обязательным ключевым словом, если необходимо, чтобы члены класса были общедоступными.

Инициализация структуры

1 способ

BOOK Novel=

{“paneless programming”,

“I.C.Fingers,

};

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

Указатель – легальная переменная.

Пропустил

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

{Struct Box

Double l,h,w;

Double volume () {return l*h*w;}

};

Box ShowIt(Box aBox)

{Box bBox;

bBox.l=2*aBox.l;

bBox.h=3*aBox.h;

bBox.w=4*aBox.w;

return bBox;

}

Int main()

{Box matchbox;

Matchbox.l=4.0;

Matchbox.h=2.5;

Matchbox.w=4.5;

Cout<<”The Volume of matchbox= “<<Matchbox.volume()<<endl;

Matchbox=showIt(matchbox);

Cout<<”l= “<<matchbox.l<<endl;

Cout<<”h= “<<matchbox.h<<endl;

Cout<<”w= “<<matchbox.w<<endl;

Cout<<”The new Volume of matchbox= “<<Matchbox.volume()<<endl;

Return 0;}

 

Замечание:

Передачу экземпляров из структуры или классов по значению можно осуществить только в том случае, если эти объекты не созданы динамической памятью с помощью операции “new”.

 

Box* ShowIt(Box* aBox)

{Box bBox=&aBox;

bBox->l=2*aBox->l;

bBox->h=3*aBox->h;

bBox->w=4*aBox->w;

return bBox;

}

Int main()

{Box matchbox=new Box;

Matchbox->l=4.0;

Matchbox->h=2.5;

Matchbox->w=4.5;

Cout<<”The Volume of matchbox= “<<Matchbox->volume()<<endl;

Matchbox=showIt(matchbox);

Cout<<”l= “<<matchbox->l<<endl;

Cout<<”h= “<<matchbox->h<<endl;

Cout<<”w= “<<matchbox->w<<endl;

Cout<<”The new Volume of matchbox= “<<Matchbox->volume()<<endl;

Return 0;}

 

Никогда!Не!Возвращать!Адрес!Локального!Объекта!

 

Ссылочный вариант:

struct Box

{

double l,h,w;

double volume () {return l*h*w;}

};

Box ShowIt(Box aBox)

{

Box bBox;

Box& rBox = bBox;

rBox.l=2*aBox.l;

rBox.h=3*aBox.h;

rBox.w=4*aBox.w;

return rBox;

}

int main()

{

Box matchbox;

Box& Rmatchbox=matchbox;

Rmatchbox.l=4.0;

Rmatchbox.h=2.5;

Rmatchbox.w=4.5;

cout<<"The Volume of matchbox= "<<Rmatchbox.volume()<<endl;

Rmatchbox=ShowIt(Rmatchbox);

cout<<"l= "<<Rmatchbox.l<<endl;

cout<<"h= "<<Rmatchbox.h<<endl;

cout<<"w= "<<Rmatchbox.w<<endl;

cout<<"The new Volume of matchbox= "<<Rmatchbox.volume()<<endl;

system ("pause");

return 0;

}

 

Все упомянутое про структуры можно использовать и в классах.

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

Для того, чтобы определить функцию вне класса, необходимо указать ее область видимости с помощью операции размещения контекста (::)

 

using std::cout;

using std::cin;

using std::endl;

 

class Box

{

public:

double l,h,w;

double volume ();

};

double Box::volume()

{return l*h*w;}

 

int main()

{

int value=0;

cout<<"enter the Value"<<endl;

cin>>value;

cout<<"your Value is "<<value<<endl;

 

system ("pause");

return 0;

}

В случае, когда функция определяется внутри определения класса, то компилятор неявно ее трактует как встроенную (inline) функцию.

Встроенная функция

…Многопропусков…


 

Присваивание параметрам в классе значений по умолчанию.

Class Bar

{

Public:

Box(){cout<<”default constructor called\n”;}

Box(double fr=1.0, double hv=1.0, double bv=1.0

{ cout<<”Constructor called\n”;

L=lv;H=hv;W=bv;}
double Volume() {return L*H*W;}

Double L,H,W;

};

Int main()

{

Box box1;

Box matchbox(1.5,2.5,2.0);

Double V=box1.volume();

Cout<<”the volume of box1=”<<V<<endl;

Cout<<”the volume of matchbox=”<< matchbox.volume()<<endl;

Return 0;

}

 

Если запустить такую программу, то мы получим сообщение об ошибке, а именно, компилятор считает, что определено множество конструкторов по умолчанию и он не может решить, какой из них следует вызывать для того, чтобы создать объект без параметров (Box1). Дело в том, что объект Box1 требует конструктор без параметров, а теперь оба конструктора могут быть вызваны без параметров. Решение подобной проблемы заключается в том, чтобы избавиться от конструктора без параметра. Закомментируем строчку под public и все заработает.

 

Class Box

{

Public:

Box(double fr=1.0, double hv=1.0, double bv=1.0

{ cout<<”Constructor called\n”;

L=lv;H=hv;W=bv;}
double Volume() {return L*H*W;}

Double L,H,W;

};

Int main()

{

Box matchbox(1.5,2.5,2.0);

Box box1(matchbox);

Double V=box1.volume();

Cout<<”the volume of box1=”<<V<<endl;

Cout<<”the volume of matchbox=”<< matchbox.volume()<<endl;

Return 0;

}

 

Допустим, мы хотим инициализировать объект box1 через matchbox. Это значит, что мы хотим создать объект box1 через matchbox. В таких случаях вызывается конструктор копирования по умолчанию. Конструктор копирования создает объект класса, инициализируя его объектом того же класса. В каких то случаях, компилятор генерирует версию конструктора копирования по умолчанию. Такой конструктор создает новый объект путем копирования существующего член за членом. Однако, этого достаточно для таких простых классов, как наш класс box, в которых не содержатся указатели или массивы, в качестве членов. В каких то случаях мы сами обязаны создать конструктор копирования.

Дружественные функции

Бывают случаи, когда по некоторым причинам необходимо, чтобы некоторые функции, не являющиеся членом класса, имели доступ к приватным членам функции. Такие функции называются дружественными функциями класса и определяются с помощью ключевого слова friend. В самом классе можно либо указать прототип дружественной функции, либо целиком ее определение. Так как дружественные функции не являются членами класса, то атрибуты доступа не относятся к ней. Это класс функций с некоторыми привилегиями (есть доступ ко всем членам класса), которыми не могут пользоваться обычные глобальные.

Примечание: в соответствии с используемым нами стилем программирования, дружественная функция должна быть описана в конце класса.

Пример:

Создадим дружественную функцию, которая будет возвращать сумму поверхностей ящика.

 

Class Box

{

Private:
double L,H,W;

Public:

Box(double fr=1.0, double hv=1.0, double bv=1.0

{ cout<<”Constructor called\n”;

L=lv;H=hv;W=bv;}
double Volume() {return L*H*W;}

Double Get_lenght();

Inline double Box::Get_length(){return L;}

Friend double BoxSurface(Box aBox);

};

Double BoxSurface(Box aBox);

Return 2*(aBox.L* aBox.H+ aBox.L* aBox.W+ aBox.H* aBox.W)

Int main()

{

Box matchbox(1.5,2.5,2.0);

Box box1(matchbox);

Double V=box1.volume();

Cout<<”the volume of box1=”<<V/box1.Get_lenght()<<endl;

Cout<<”the volume of matchbox=”<< matchbox.volume()<<endl;

Return 0;

}

 

Интересным применением указателя this является недопущения присваивания самому себе. В дальнейшем мы увидим, что присваивание объекта самому себе может вызвать серьезные ошибки, если объекты содержат указатели на выделенную динамическую память.

Статические члены класса

 

Double sum (double a, double b)

{ static int count=0;

Count++;

Cout<<”this function has been called ”<<count<<”times”<<endl;

return a+b;}

int main()

{

Sum(5.0,5.0);

Sum(5.0,5.0);

Return 0;

}

 

Статические члены класса

Статические данные-члены

Статические функции-члены

 

Статические данные-члены дают такой эффект, что они создаются в единственном экземпляре и разделяются всем объектами класса.

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

Static int count; - статическая переменная, по умолчанию имеет значение 0, существует во время выполнения всей программы. Локальные же имеют по умолчанию «мусорное» значение, существуют только в своем блоке программы.

Одним из практических применений статических переменных является подсчет количества вызовов функции.

Double sum (double a, double b)

{

static int count;

Count++;

Cout<<”sum was been called ”<<count<<” times”<<endl;

Return a+b;

}

Int main()

{

sum (5,3);

Sum(10,3);

Return 0;

}

 

Class Box

{

Public:

Static int count;

Private:

Double L,H,W;};

 

Одним из практичских применений статических переменных является подсчет существующих объектов класса. Для этого его нужно поместить в общедоступный раздел класса.

Как инициализировать данный объект класса?

Нельзя инициализировать в самом классе, т.к. класс общая модель для создания любого объекта класса. Мы не можем его инициализировать в конструкторе т.к. мы желаем его значения увеличивать при каждом вызове конструктора для создания объектов.

Мы не можем инициализировать его в другом функции-члене, т.к. она будет ассоциироваться с объектом, а инициализация этого статического члена нужна для любого объекта. Значит статический данный-член нужно инициализировать вне класса с помощь оператора int Box::count=0;

Class Box

{

Public:

Static int count;

Box(double pv, double hv=1.0, double bv=1.0)

{ cout <<”constructor called”;

L=H=W=1.0;

Count++;}

 

Couble Volume() {return L*H*W;}

Private:

Double L,H,W;

};

 

Int Box::count=0;

 

Int main()

{

Box boxes[5];

Box cigar (2.0, 5.0, 1.0);

Cout<<”\n\n Number of objects (through class)=”<<box.count<<endl;

Cout<<”\n\n Number of objects (through object)=”<<boxes[2].count<<endl;

Return 0;

}йомахарт ймасоул nhgjhygjuloilhgbg

 

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

 

Статические функции-члены класса.

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

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

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

Static int Function(int N);

aBox.Function(5);

Box::Function(5);

Массив объекта

Массив может хранить объекты любого типа, как встроенного, так и собственного пользовательского типа. При объявлении массива компилятору сообщают количество хранимых объектов (размер массива), что позволит выделить участок памяти требуемого размера и тип объекта. Для того, чтобы объекты могли быть созданы при определении массива, класс должен обладать конструктором по умолчанию без параметров. Для обращения к членам массива сначала используется операция индексации ([..]), а затем, с помощью операции прямого доступа, получают доступ к определенному члену класса. Количество выделенной памяти для всего массива, компьютер выделяет на основании типа.

 

Int main()

{

Box boxes[5];

Box cigar(8.0,5.0,1.0);

Boxes[3]=cigar;

Cout<<”the volume of boxes[1]= “<<boxes[1].Volume()<<endl;

Cout<<”the volume of boxes[4]= “<<boxes[4].Volume()<<” =”Volume of cigar= “<<cigar.Volume()<<endl;

Return 0;

}

 

Деструкторы

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

 

Class Box

{public:

~Box(){cout<<”destructor called/n”;}

Box(double lv=1.0, double hv=1.0, double bv=1.0)

{L=lv;H=hv;W=bv;

Cout<<”constructor called\n”;}

Double Volume(){return L*H*W;}

Private:

Double L,H,W;

};

Int main()

{ Box boxes[5]

Box cigar(8.0,5.0,1.0);

Box match(2.2,1.1,0,5);

Box* PB1=&cigar;

Box* PB2=Null;

Cout<<endl<<”the cigar Volume= ”<<PB1->Volume()<<endl;

PB2=boxes;

Boxes[2]=match;

Cout<<”the volume of boxes[2]= “<<(PB+2)->Volume()<<endl;

Return0;

}

Замечание:

При присваивании указателю PB2 массива boxes, происходит передача адреса первого элемента массива boxes.

При вызове Volume(), для выбора необходимого нам 3-го элемента массива, мы можем просто прибавить к нашей позиции(1-й элемент) еще 2 позиции и получим 3-й элемент.

Перегрузка операции

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

Приоритет выполнения операций:

::

() [] {}

* /

+ -

=

> < >= <= ==

?:

 

Z=(a>b)?a:((a>=c)?b:((c<d)?d:a)))

 

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

1 -::

2 -?:

3 -. операция прямого доступа к членам класса

4 – sizeof

5 - * операция разыменования

Перегруженные операции могут быть либо методом класса, либо как обычные глобальные функции, однако следующие перегруженные операции могут быть только функциями-членами класса:

1 - = присваивание

2 – () операция вызова функции

3 – [] операция индексирования

4 - -> операция косвенного доступа к членам класса

Перегруженные операции определяются с помощью ключевого слова operator

<тип возврата> Operator <операция>(<список параметров>)

{тело функции}

Левые функции

Lvalue (left value)

Rvalue (right value)

X+Y=Z неверно

Z=X+Y верно

W=2Z верно

Const double PI=acos(-1.0);

Double S=2*PI*pow(r,2);

PI=… неверно в связи с тем, что выше указано то, что PI – константа

Int A[10]={0,1,2,3,4,5,6,7,8,9};

A[2]=A[9];

 

Int Sum(int a,int b)

{Return a+b;}

Int main()

{int s=sum(5,10);

}

 

Вызов функции, возвращающей значение, обычно используется в некотором выражении, как например вот здесь. Однако, в С++ функция может возвращать ссылку. Такая возможность появилась исключительно в силу необходимости перегрузки операции присваивания и индексации. Функции, которые возвращают ссылку, могут стоять и справа и слева от знака присваивания. Рассмотрим некоторые проблемы, возникающие при ее использовании. Следующее определение функции приводит к непредсказуемым результатам при выполнении функции.

 

Int &ff(int k)

{return k;}// неправильная функция, возвращающая ссылку, т.к. она возвращает адрес локальной переменной. Но тем самым при запуске программы мы получим только предупреждение, что мы возвращаем адрес локальной переменной, но позволяет запустить программу.

Int f(int &i)

{return I;}// здесь получается правильное значение 5

Int main()

{

Int n=5;

Cout<<f(ff(n))<<endl;

Int m=2

Cout<<f(g)<<endl;

Return 0;

}

 

Как решить эту проблему? Существует 2 способа решения данной проблемы:

1) Передавать параметры по ссылке Int &ff(int &k)

2) Возвращать ссылку на статическую переменную:

{static int a; a=k; return a;}

Второй вариант решения данной проблемы очевиден, т.к. статические переменные будут существовать всё время выполнения программы, не смотря на то, что у них локальная область видимости.

 

Здесь были Регинины каракули…

 

Тривиальный пример:

Левая функция для выбора максимума двух чисел.

Здесь тоже они были…

Double& max(double &x, double &y)

{return (x>y)?x:y;}

{double Z=0.0;

Z=max(10.0,15.0);// обычный вызов

Double a=5, b=6, c=7;

Max(a,b)=10.0; // b=10.0

Max(max(a,b),c)=10 //a=10

Max(max(a,b)=10,c)=0 //b=0.0

//левая функция для минимума массива

Int &Mmin (int d[], int n)

{ int im=0

For (int i=0;i<n;i++)

Im=d[im]<d[i]?im:i;

Return d[im];}

Int main ()

{int x[10]={10,20,40,5,6,7,8,9,50,12}

Int m=Mmin(x,10);

Cout<<”m=”<<m<<endl;

Mmin(x,10)=0; // Минимальный элемент будет заменен на 0

}

 

Перегрузка операции присваивания.

Если мы не представим свою версию операции присваивания, то компилятор использует свою версию, сгенерированную по умолчанию. Версия по умолчанию работает подобно конструктору копирования по умолчанию. А именно выполняет процесс присваивания (копирования) член за членом. Однако, это не одно и то же.

Конструктор копирования по умолчанию и перегруженная операция присваивания по умолчанию – разные вещи. Конструктор копирования по умолчанию вызывается, когда один объект инициализируется другим объектом или при передаче объекта в функцию по значению. А операция присваивания по умолчанию вызывается когда левая и правая части операции присваивания являются объектами одного и того же класса.

Для примера класса Box операция присваивания работает без проблем. Но для других классов, в которых память для членов выделяется динамически, может возникнуть потенциальная угроза породить хаос в программе.

Box box1 (4.0,2.0,3.0);

Box box2=box1;// операция присваивания по умолчанию сработает адекватно

 

Message mess1(“The Visual studio 2010”);

Message mess2=mess1;// могут возникнуть проблемы

 

Эффект от применения операции присваивания по умолчанию для объектов класса типа Message будет такой же, как и от использования конструктора копирования по умолчанию. Возникнут проблемы, поскольку каждый объект имеет указатель на одну и ту же строку. И если строка изменится в одном объекте, то изменится в обоих. Кроме того, если один объект разрушается деструктором при выходе из области видимости, то его деструктор освобождает память, использованную для строки, на которую указывает указатель другого объекта. Но память уже была освобождена и, возможно, уже используется для других целей в другом месте. Поэтому в таких случаях необходимо представить собственную версию перегрузки операции присваивания.

В теле класса Message следует добавить оператор:

Message &operator=(const Message &aMess)

{delete [] pmessage;

Pmessage=new char[strlen(aMess.pmessage)+1];

Strcpy(this->pmessage, aMess.pmessage);

Return *this;}

 

Почему тип возврата функции operator= должна быть ссылка?

Чтобы ответить на этот вопрос, рассмотрим 2 варианта присваивания.

Допустим, что есть 3 объекта типа message.

Mess3=mess2=mess1;

 

… пропуск …

 

Постановка задачи:

Определим сумму двух объектов типа Box как объект Box достаточно большой, чтобы вместить два других, поставленных один на другой. Для этого параметры нового объекта типа Box будут определяться с помощью следующих объектов.

Lbox3=max(Lbox1,Lbox2);

Wbox3=max(Wbox1,Wbox2);

Hbox3=Hbox1+Hbox2;

 

Class Box

{

Public:

Box(double lv=1.0, double wv=1.0, double hv=1.0):

L(lv), W(wv), H(hv)

{L=lv>wv?lv:wv;

W=wv<lv?wv:lv;}

double volume() {return L*W*H;}

Box operator +(const Box& aBox, const Box& Bbox) const;

Return Box(L>aBox.L?L:abox.L, W>aBox.W?W:abox.W,H+=aBox.H);}

Void ShowIt()

{cout<<L<<setw(5)<<W<< setw(5)<<H;}

Private: double L,W,H;};

 

Int main()

{Box smallB(4.0,2.0,1.0);

Box medium (10.0,4.0,2.0);

Box aBox, bBox;

aBox=smallB+medium;

cout<<”the dimentions of abox:\n”;

aBox.Show It();

cout<<endl;

bBox=smallB+medium+aBox;

cout<<”the dimentions of bbox:\n”;

bBox.Show It();

cout<<endl;

return 0;

 

Для описания перегрузки оператора сложения вне тела класса, создадим дружественную глобальную функцию, не забыв объявить прототип функции в теле класса.

 

Class Box

{

Public:

Box(double lv=1.0, double wv=1.0, double hv=1.0):

L(lv), W(wv), H(hv)

{L=lv>wv?lv:wv;

W=wv<lv?wv:lv;}

double volume() {return L*W*H;}

friend Box operator +(const Box& aBox, const Box& Bbox) const;

Void ShowIt()

{cout<<L<<setw(5)<<W<< setw(5)<<H;}

Private: double L,W,H;};

 

Box operator +(const Box& aBox, const Box& Bbox) const;

Return Box(L>aBox.L?L:abox.L, W>aBox.W?W:abox.W,H+=aBox.H);}

Шаблоны классов

 

… пол пары пропуска …

Пример, иллюстрирующий использование шаблона:

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

Template <class T>

Class Samples

{ public:

Samples (const T& Values [], int const) – конструктор, принимающий массив выборок.

{Free=(count<100)?count:100;

For (int i=0; i<Free, i++)

Values[i]=values[i];}

 

Samples (const T& Value) – одну выборку

{Values[0]=Value; Free=1;};

 

Sample(){Free=0;} – пустой конструктор по умолчанию

 

Bool Add (const T& value)

{bool OK=Free<100;

If(OK) Values[Free++]=value; return OK;}

 

T Max()const

{T theMax=Free? Values[0]:0;

For (int i=1; i<Free; i++)

{if (Values[i]>theMax) theMax=Values[i];

Return theMax;}

 

Private: T Values[100]; int Free;};

 

Примечание: всегда можно определить шаблон, который работает только с некоторыми типами, а с другими не работает. Но нет способа ограничить типы, применимые с конкретным шаблоном.

 

Int main(){Box boxes[]={box(8.0,5.0,2.0), box(5.0,4.0,6.0), box(4.0,3.0,3.0);}

Samples <Box>myboxes(boxes, size of boxes/size of boxes[0])

Cout<<endl<<”The Volume of the most bigger box is “<<myBoxes.Max()<<endl;

Return 0;}

 

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

 

Пропуски:

22.02

07.03

02.05

30.05

Методология ООП

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

Основное требование к модели программы – она должна быть понятна заказчику программы, пользователю и группе программистов – разработчиков.

Объектно-ориентированное программирование – совокупность технологий, принципов, инструментальных средств, для создания программных систем на основе архитектуры взаимодействия объектов.

Основные принципы ООП:

1) Абстракция

2) Инкапсуляция

3) Наследование

4) Полиморфизм (многообразие)

 

Пример описания модели:

Class Box

{

Public:

Double H;

Double L;

Double W;

Double volume ()

{return H*L*W;}

};

 

Абстракция

– характеристика сущности, которая отличает ее от других сущностей. Абстракция определяет границу представления соответствующего объекта модели и применяется для определения таких фундаментальных понятий ООП как класс или объект.

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

Класс представляет собой абстракцию совокупности реальных объектов, которые имеют общий набор свойств и обладают одинаковым поведением.

Объект (в контексте ООП) рассматривается как экземпляр соответствующего класса.

Объекты, которые не имеют идентичных свойств или не обладают одинаковым поведением, по определению не могут быть отнесены к одному классу.

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

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

Пример обозначений класса и связи между базовым и производным классом на языке UML. Стрелка указывает на базовый класс от производного.

Инкапсуляция

- Характеризует сокрытие отдельных деталей внутреннего устройства классов от внешних по отношению к нему объектов или пользователей. Инкапсуляция ведет свое происхождение от деления модулей, в некоторых языках программирования, на 2 части: интерфейс и реализацию. При этом в интерфейсной части модуля описываются все объявления, функций и процедур, а возможно и типов данных, доступных за пределами модуля. Указанные процедуры и функции являются способами оказания услуг внешним клиентам. В секции реализации содержится программный код, определяющий конкретные способы реализации объявленных в интерфейсной части процедур и функций. Объявление функции называется прототипом функции.

 

Полиморфизм

- понимается свойство объектов принимать различные внешние формы, в зависимости от обстоятельств. Применительно к ООП, полиморфизм означает, что действия, выполняемые одноименными методами, могут отличаться, в зависимости от того, к какому классу относится тот или иной метод.

Полиморфизм объектно-ориентированных языков связан с перегрузкой функции, но не тождественен ей. Важно понять, что имена методов и свойств тесно связаны с классами, в которых они описаны.

Полиморфизм тесно связан с механизмом работы виртуальных функции.

Double Sum (double a, double b)

{return a+b;}

Int Sum (int a, int b)

{return a+b;}

Double Sum (double* a, double b)

{double s=0.0;

For (int i=0; i<n; i++)

{s+=a[i]; return s;}

Double Sum (double a[10])

{double s=0.0;

For (int i=0; i<10; i++)

{s+=a[i];

return s;}

 

Нужная функция будет определена по значениям переменных.

Типы возвращаемого значения не относятся к типу функции.

Int main()

{ double* a=new double[10];

Sum (a,10);

Sum(a[10]);

Sum(10,10);

Sum(10.0,10.0);

}

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

Определение собственных типов данных

Структура и класс.

Структура – собственный или пользовательский тип данных, определяемы с помощью ключевого слова “struct”.

Класс – тоже пользовательский или абстрактный тип данных, определяемый с помощью ключевого слова “class”.



Поделиться:


Последнее изменение этой страницы: 2017-02-07; просмотров: 106; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.133.146.143 (0.466 с.)