Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Sb97809

.pdf
Скачиваний:
2
Добавлен:
13.02.2021
Размер:
441.54 Кб
Скачать

cout<<v[i];}

main(void)

{vector v1(1,2,3), v2(10,11,12),v3; v3=v1+v2;

for (int i=0;i<3;i++) cout<<v3[i]; v3.show(); v1[0]=100; v1[1]=201; v2[2]=302; v1.show();}

12. АРГУМЕНТЫ ПО УМОЛЧАНИЮ

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

Вспомним перегруженные конструкторы в классе data: date (int day, int month, int year);

date (int); date (void);

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

date (int day=1, int month=12, int year=2000);

Вновой версии определения объектов дают одинаковые результаты. date A(1,12,200); date B(1); date C();

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

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

21

после первого аргумента по умолчанию все последующие аргументы в списке также должны быть аргументами по умолчанию.

Пример:

 

date D( , ,200); date E(8, , 2018);

/ выдает сообщение об ошибке

13. УКАЗАТЕЛИ НА ПРОИЗВОДНЫЕ ТИПЫ

Указатели на производные типы – это одна из форм реализации динамического полиморфизма в С++. Указатель на базовый тип и производный тип

зависимы.

 

Например: B_class

C_class

В С++ всякий указатель на базовый класс В может быть также указате-

лем на производный класс D.

 

B_class *p;

 

B_class b_ob;

 

D_class D_ob;

 

p=&B_ob;

 

p=&D_ob;

 

((D_class*)p)->f();

– приведение типа

При создании указателя на объект базового класса происходит присвоение как адреса объекта базового класса, так и производной класса.

Все элементы класса D, наследуемые от класса B, могут быть доступны через использование указателя p.

Если требуется иметь доступ к элементам, используется указатель на базовый класс, который надо привести к указателю на произвольный тип.

p – указатель на базовый класс; f – функция-член класса.

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

При использовании операций Инкремент, Декремент указатель изменяется относительно базового типа.

Когда указатель на базовый класс указывает на производный, инкремент не делает его указывающим на следующий элемент производного класса.

13.1. Виртуальные функции

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

22

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

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

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

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

# include<iostream.h> class figure{

protected: double x,y;

public:void set_dim(double i, double j=0) x=i;

y=j;}

virtual void show_area(){ cout<<”площадь не определена\n”;}}; class triangle:public figure{

public:void show_area(){

cout<<”треугольник с высотой”<<x<<”и основанием”<<y; cout<<”имеет площадь”<<x*0,5+y;}};

class rectangle:public figure{ public:void show_area(){

cout<<”прямоугольник со сторонами”<<x<<y; cout<<”имеет площадь”<<x*y;}};

23

class circle:public figure{ public:void show_area(){ cout<<”круг с радиусом”<<x;

cout<<”имеет площадь”<<3.14*x*x;}}; main()

{figure f,*p; triangle t; rectangle s; circle c; p=&f;

p->set_dim(1,2); p->show_area(); p=&t;

p->set_dim(3,4); p->show_area(); p=&s;

p->set_dim(5,6); p->show_area(); p=&c;

p->set_dim(8); p->show_area();

}

Программа позволяет определить площади некоторых плоских фигур с использованием виртуальной функции. Виртуальная функция – определение площади.

13.2. Чистые виртуальные функции и абстрактные типы

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

virtual тип имя_ф (список параметров)=0;

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

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

24

# include<iostream.h> class figure{

protected: double x,y;

public:void set_dim(double i, double j=0) x=i;

y=j;}

virtual void show_area()=0;}; class triangle:public figure{ public:void show_area(){

cout<<”треугольник с высотой”<<x<<”и основанием”<<y; cout<<”имеет площадь”<<x*0,5+y;}};

class rectangle:public figure{ public:void show_area(){

cout<<”прямоугольник со сторонами”<<x<<y; cout<<”имеет площадь”<<x*y;}};

class circle:public figure{ public:void show_area(){ cout<<”круг с радиусом”<<x;

cout<<”имеет площадь”<<3.14*x*x;}}; main()

{figure f,*p; triangle t; rectangle s; circle c; p=&t;

p->set_dim(3,4); p->show_area(); p=&s;

p->set_dim(5,6); p->show_area(); p=&c;

p->set_dim(8); p->show_area();

}

13.3. Виртуальные базовые классы

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

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

25

базовые классы перечисляются через запятую, при создании объекта, конструкторы выполняются в порядке следования базовых классов слева направо.

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

class X:public Base{…}; class Y:public Base{…};

class D:public X, public Y{…};

Base Base

X Y

D

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

class X: virtual public Base{…}; class Y: virtual public Base{…};

class D: virtual public X, public Y{…};

Base

X Y

D

Класс D имеет один подобъект класса Base.

14. ШАБЛОНЫ

Шаблоны – это одна из реализаций полиморфизма в С++. Шаблон представляет собой специальное описание родовой (параметризированной) функции или родового класса, в которой информация об используемых в реализации типов данных преднамеренно остается незаданной.

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

26

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

Шаблон создается при помощи ключевого слова template и угловых скобок < > со списком параметров, за которым следует описание класса или функции.

Template <class фикт_имя1, class фикт_имя2 …>

Определение;

Вместо ключевого слова class допускается использование ключевого слова typename. Шаблоны тождественны интеллектуальным макросам, которые способны сообщить компилятору о каждом случае использования шаблона новых типов данных.

14.1. Шаблоны функций

Пример: определить максимальное значение из двух аргументов max(x,y).

Способы реализации: 1. Макрос

#define max(x,y)

(((x)>(y)?(x): (y))

Преимущество: можно сравнивать данные различных типов. Недостаток: позволяет сравнивать несовместимые типы данных, т. е. от-

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

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

Template <class T> T max(Tx,Ty)

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

Здесь тип данных представляется аргументом класса T. Значение Т – фиктивное имя типа данных, которое программист выбирает по своему усмотрению.

Компилятор сгенерирует соответствующий код функции max() согласно действительному типу данных, используемому при вызове функции.

Пример:

#include<string.h>

#include<iostream.h>

27

template<class T> T max(Tx,Ty) {return(x>y)?x:y;}; int main()

{int a=5, b=30, c=15, d=0; float pi=3.14, e=2.172;

cout<<”наиб.2х целых”<<max(d,a); d=max(a,max(b,c));

cout<<”наиб.3х целых”<<d; cout<<”max(pi,e)=”<<max(pi,e); char m[]=”мама”,p[]=”папа”,*s; s=max(p,m);

cout<<”кто в доме хозяин?”<<s;

}

Для приведенной программы компилятор автоматически сгенерирует по шаблону три перегруженных функции с типами:

int max(int x,int y); float max(float x,float y);

char * max(char *x, char *y);

Экземпляр функции создается каждый раз при вызове функции с аргументами, для которых определение не найдено. Если имеется точное определение обычной функции, то в ее вызов вставляется тело программы. Особенности возникают, когда в качестве параметров передаются строки. Функция сравнивает два указателя, а не содержимое строк в кодировке ASCII “папа”>”мама”. В действительности результат зависит от того, в какой последовательности компилятор размещает в памяти указанные строки.

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

char *max(char *x, char *y) {return (strcmp(x,y)>0?x:y;}

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

28

14.2.Использование шаблонов с двумя типами параметров

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

Template <class T1, class T2>

T1 max(T1x, T2y) {if (x>=y) return x; return y;}

main()

{int i=5; long l=123456; float d=5.5;

cout<<”максимум =”<<max(d,i); cout<<”максимум =”<<max(l,i);

}

Обращение типа max(i,l), max(i,d) даст неверный результат. Возвращаемое значение не поместится в переменную типа int.

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

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

Пример: class vector не зависит от типа данных, производится инициализация получения элемента по индексу.

template<class T> class vector

{T *data; int size; public:vector(int); ~vector()

{delete[] data;} T& operator[](int i) {return data[i];}

29

};

template <class T> <vector T> vector(int n)

{data=new T[n]; size=n;};

int main() {vector<int>x(10); for (int i=0;i<0;++i) x[i]=i*i; vector<char> c9150;

for (charic=0;i<<5;++ic) c[i]=ic+’a’;}

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

class vector<char *>{…};

Для вектора строк класс по шаблону генерироваться не будет.

15. ЧТЕНИЕ И ЗАПИСЬ ФАЙЛОВ

Файл – это поименованная область хранения информации, обычно на диске. В С++ операции ввода-вывода организованы посредством библиотечных функций:

ANSI – буферизованный ;

UNIX – неформатированный; C++ – объектно-ориентированный.

15.1. Файл и поток

Система ввода–вывода в С++ не зависит от типа устройств ввода– вывода. Есть абстрактный уровень между программистом и физическим устройством, называемый потоком.

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

Разделяют текстовый и двоичный потоки.

Текстовый поток – это последовательность символов; нет однозначного соответствия между символами в потоке и на экране. Например, при нажатии

30

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]