Доступ к наследуемым членам

Пример

class student {

protected:

char fac[20];

char spec[30];

char name[15];
public:

student(char *f, char *s, char *n);

void print();
};
class grad_student: public student {

protected:

int year;

char work[30];
public:

grad_student(char *f, char *s, char *n, char *w, int y);

void print();
};

Порожденный класс наследует все данные класса student, имеет доступ кprotected и public-членам базового класса. В новом классе добавлено два член-данных, и порожденный класс переопределяет функцию print().

student:: student(char *f, char *s, char *n) {

strcpy(fac, f);

strcpy(spec, s);

strcpy(name, n);
}
grad_student:: grad_student(char *f, char *s, char *n, char *w, int y):

student(f,s,n) {

year = y;

strcpy(work, w);
}

Конструктор для базового класса вызывается в списке инициализации.
Перегрузка функции print().

void student:: print() {
cout << endl << "fac: " << fac << " spec: " << spec
<< " name: " << name;
}
void grad_student:: print() {
student:: print();
cout << " work: " << work << " year: " << year;
}
int main() {

system("chcp 1251");

system("cls");

student s("МТ", "АМСП", "Сидоров Иван");

grad_student stud("ПС", "УиТС", "Иванов Петр", "Метран", 2000);

student *p = &s;

p->print();

grad_student *gs = &stud;

student *m;

gs->print();

m = gs;

m->print();

cin.get();

return 0;
}

Результат выполнения

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

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

Неявные преобразования между порожденным и базовым классами называются предопределенными стандартными преобразованиями:

§ объект порожденного класса неявно преобразуется к объекту базового класса.

§ ссылка на порожденный класс неявно преобразуется к ссылке на базовый класс.

§ указатель на порожденный класс неявно преобразуется к указателю на базовый класс.

Доступ к наследуемым членам.

Доступ извне.

Доступными являются лишь элементы с атрибутом public.

Собственные члены класса.

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

Наследуемые члены класса.

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

Пример.

class Basis{public: int a;protected: int b, c;};class Derived: public Basis{public: Basis::c;};int main (void){ Basis ob; Derived od; ob.a; // правильно ob.b; // ошибка od.c; // правильно od.b; // ошибка return 0;}

Доступ изнутри.

Собственные члены класса.

private и protected члены класса могут быть использованы только функциями-членами данного класса.

Наследуемые члены класса.

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

protected или public члены класса доступны для всех функций-членов. Подразделение на public, protected и private относится при этом к описаниям, приведенным в базовом классе, независимо от формы наследования.

Пример

class Basis{public: void f1(int i) { a = i; b = i; } int b;private: int a;}; class Derived: private Basis{public: void f2(int i) { a = i; // ошибка b = i; // правильно }}; Статусы доступа при наследовании классов

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

Используются следующие соглашения:

– private-компоненты доступны только внутри того класса, в котором они определены;

– protected-компоненты доступны внутри того класса, в котором они определены, а также во всех производных классах;

– public-компоненты видны во всех частях программы.

При описании можно изменить статус доступа к наследуемым компонентам (только в направлении ужесточения). Формат описания производного класса с единичным наследованием имеет вид:

class имя_производного_класса: [модификатор] имя_базового_класса

{компоненты_производного_класса};

31. Создание многомодульного проекта.

32. Классы ввода/вывода в С++. Примеры использования. Классы ввода-вывода образуют иерархию по принципу наследования. Базовым в этой иерархии является класс ios (исключение составляют лишь классы буферизированных потоков). В классе ios объединены базовые данные и методы для ввода-вывода. Прямыми потомками класса ios являются классы istream и ostream. Класс istream — это класс входных потоков; ostream — класс выходных потоков. Потомком этих двух классов является iostream — класс двунаправленных потоков ввода-вывода. Объект cout принадлежит к классу ostream и представляет собой поток вывода, связанный с дисплеем. Объект cin принадлежит классу istream и является потоком ввода, связанным с клавиатурой. Оба эти объекта наследуются классом iostream.

Знак << обозначает перегруженную операцию вставки символов в поток вывода cout, а >> — знак операции извлечения из потока ввода cin.

Для организации форматированного потокового ввода-вывода в Си++ существуют два средства:

• применение функций-членов класса ios для управления флагами форматирования;

• применение функций-манипуляторов.

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

Начальная часть таблицы, выводимой по этой программе, имеет вид:

33.Полиморфизм. Механизм виртуальных функций. Привести примеры.

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

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

§ внешняя общность проявляется как эквивалентный набор методов с одинаковыми именами или совпадающими сигнатурами (именами методов, типами аргументов и их количеством);

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

Механизм виртуальных функций.

Student s;
Person &p = s;
s.name(); //Student::name()
p.name(); //Person::name()

В 3-й строке вызовется метод класса Student, т.к. s является объектом этого класса. Однако, в строке 4 вызовется метод name базового класса Person, хотя по логике следовало бы тоже ожидать вызов name() класса Student — ведь p — это ссылка на объект производного класса.

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

struct Person
{
virtual string name() const;
};

struct Student: Person
{
string name() const;
};

Перед методом name класса Person мы указали ключевое слово virtual, которое указывает, что метод является виртуальным. Теперь при вызове p.name() произойдет вызов метода класса Student, несмотря на то, что мы его вызываем через ссылку на базовый класс Person. Аналогичная ситуация и с указателями:

Student s;
Person *p = &s;
p->name(); //вызовется Student::name();
Person n;
p = &n;
p->name(); //вызовется Person::name()

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

Механизм виртуальных функций реализует полиморфизм времени выполнения: какой виртуальный метод вызовется будет известно только во время выполнения программы.

В качестве следующего примера можно рассмотреть класс TextFile, от которого наследуются два класса: GZippedTextFile и BZippedTextFile. Базовый класс имеет два метода: name(), возвращающий имя файла, и read(), считывающий данные из файла. В этом случае виртуальным имеет смысл сделать только метод read, т.к. у каждого типа сжатого файла будет свой способ считывания данных:

struct TextFile
{
string name() const;
virtual string read(size_t count);
//...
};

struct GZippedTextFile: TextFile
{
string read(size_t count);
//...
}

struct BZippedTextFile: TextFile
{
string read(size_t count);
//...
}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: