Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Наследование. Дружественные функции




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

Ключевое слово friend (друг) служит спецификатором, уточняющим свойства функции. Оно дает функции-не-члену доступ к скрытым членам класса и предоставляет способ для обхода ограничений сокрытия данных в C++. Однако должна быть веская причина для обхода этих ограничений, поскольку они важны для надежного программирования.

Одна из таких причин состоит в том, что некоторые функции нуждаются в привилегированном доступе более чем к одному классу. Например, нам нужно определить оператор, который умножает матрицу Matrix на вектор Vector. Представление Matrix и Vector скрыто (private:)

 

class Matrix;

 

class Vector{

float v[4];

//...

friend Vector operator*(const Matrix&,const Vector&);

};

 

class Matrix{

Vector v[4];

//...

friend Vector operator*(const Matrix&,const Vector&);

};

 

Vector operator*(const Matrix& m,const Vector& v)

{

Vector r;

for(int i=0;i<4;i++){

r.v[i]=0;

for(int j=0;j<4;j++)r.v[i]+=m.v[i].v[j]*v.v[j];

}

return r;

}


 

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

Многие полезные типы являются вариантами других, и часто бывает утомительно создавать для каждого из них один и тот же код. Производный класс наследует описание базового класса; затем он может быть изменен добавлением новых членов, изменением существующих функций-членов и изменением прав доступа. Эта концепция полезна так же, как полезна таксономическая классификация: и слон и мышь являются млекопитающими. Информация о том, что млекопитающие – теплокровные высшие позвоночные, подробно изложена лишь однажды – в базовом понятии. В понятиях «слон» и «мышь» даются лишь характерные особенности этих животных.

Производный класс

Класс можно сделать производным от существующего с использованием следующей формы:

class имя_класса: (public|protected|private)opt имя_базового_класса

{

объявления членов

};

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

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

Рассмотрим пример:

<сначала – только члены данных>

<потом – функции-члены>

<потом – конструкторы>

//inheritance.h

class employee

{

public:

employee (char *name, double salary);

void print()const;

private:

char nm[30];

double slry;

};

 

enum language {ASSEMBLER,BASIC,PASCAL,C,CPP,SMALLTALK};

 

class programmer1

{

public:

programmer1(char *name, double salary, language lang);

void print()const;

private:

char nm[30];

double slry;

language lng;

};

 

class programmer2:public employee

{

public:

programmer2(char *name, double salary, language lang);

void print()const;

private:

language lng;

};

 

 

//inheritance.cpp

#include "inheritance.h"

 

employee::employee(char *name, double salary)

:slry(salary)

{

strcpy(nm,name);

}

 

void employee::print()const

{

cout<<endl<<"Name: "<<nm<<", Salary: "<<slry<<"$";

}

 

programmer1::programmer1(char *name, double salary, language lang)

:slry(salary),lng(lang)

{

strcpy(nm,name);

}

 

void programmer1::print()const

{

cout<<endl<<"Name: "<<nm<<", Salary: "<<slry<<"$, Language:";

switch(lng){

case C:cout<<"C";break;

case CPP:cout<<"C++";break;

default:cout<<"Another language";break;

}

}

 

programmer2::programmer2(char *name, double salary, language lang)

:employee(name,salary),lng(lang)

{}

 

void programmer2::print()const

{

employee::print();//печать информации о работнике

// должен использоваться оператор::, потому что

// print() была замещена в programmer2

 

// ошибка:

// cout<<endl<<"Name: "<<nm<<", Salary: "<<slry<<"$, Language:";

// нельзя обратиться к закрытым членам базового класса

 

cout<<", Language:";//печать информации, относящейся

switch(lng){ // только к программисту

case C:cout<<"C";break;

case CPP:cout<<"C++";break;

default:cout<<"Another language";break;

}

}

Производный класс programmer2 имеет дополнительно 1 член данных lng и переопределяет функцию-член print(). Эта функция замещается, то есть производный класс включает реализацию функций-членов, отличную от базового класса. Это не имеет ничего общего с перегрузкой, когда смысл одного и того же имени функции может быть различным для разных сигнатур.

 

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

 

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

 

employee e1("John",80),e2("Ben",90);

v.print();p.print();

programmer1 p1("Ron",200,BASIC);

p1.print();

programmer2 p2("Michael",500,C);

p2.print();

Как видите, использование классов programmer1 (без наследования) и programmer2 (с наследованием) здесь не отличается. Однако наследование имеет преимущество. Определение класса programmer2 как производного от employee делает его подтипом employee. Это значит, что объектом programmer2 можно пользоваться везде, где допустим employee. Например:

print_in_report(const employee& e){ e.print(); }

print_in_report(e1);

//print_in_report(p1); //error

print_in_report(p2); //OK

 

Копирование и срезка

employee arr_emp[3]={e1,p2,e2};

mas[0]=v;

// mas[0]=p1;//error

for(int i=0;i<3;++i)mas[i].print();

 

programmer2 p;

employee r=p;//срезка

 

Копируется только employee-часть programmer2. Этот эффект называют срезкой. Часто он является нежелательным. Одной из причин использования указателей и ссылок на объекты в иерархии является желание избежать срезки. Например:

employee *rptr=&r;//адрес работника

rptr=&p;//адрес программиста. Срезка отсутствует

 

Производные классы (Derived* можно использовать как Base*)

Функции-члены (замещение, обращение к ф-членам базового кл.)

Конструкторы и деструкторы (инициализация членов базового класса через конструктор базового класса)

 




Поделиться с друзьями:


Дата добавления: 2014-01-11; Просмотров: 1284; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.068 сек.