Оголошення доступу

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

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

 

class Base

{

int a;

void g();

public:

int b,c;

void f(), f(int), g(int);

};

 

class Derived: private Base

{

// ...

public:

Base::a; // помилка: не можна зробити 'a' public

Base::b; // знову робить 'b' public

int c;

Base::c; // помилка: 'c' оголошується двічі

Base::f; // знову робить всі f() public

Base::g; // помилка: всі функції g() мають різні обмеження

// доступу

};

5. Покажчики і посилання

Для базового класу вказують або посилаються на об’єкт похідного класу.

6. Віртуальні функції-члени

Віртуальні функції-члени дозволяють вибирати члени з тим самим ім’ям через покажчик функції залежно від типу об’єкта, на який вказує покажчик, а не залежно від типу покажчика.

Віртуальні функції-члени дозволяють використовувати об’єктно-орієнтовану парадигму програмування.

Типи аргументів, їхня кількість, а також тип значення, що повертається, віртуальної функції-члена повинні бути такими ж, як і в одноіменної віртуальної функції в базовому класі.

Віртуальні функції-члени не можуть бути статичними.

 

class Phone

{

// ...

public:

virtual void GiveDialTone(); // віртуальна функція

// ...

};

 

class PayPhone: public Phone

{

// ...

public:

void GiveDialTone();

};

 

Phone home(516, 555, 8858);

PayPhone booth(708, 533, 5444);

Phone* p; // покажчик на будь-який вид Phone

p = &home; // тут ніяких сюрпризів немає

p->GiveDialTone(); // виклик Phone::GiveDialTone();

p = &booth; // а тут щось таємниче

p->GiveDialTone(); // виклик PayPhone::GiveDialTone();

 

Віртуальні функції-члени можуть використовуватися для створення абстрактних базових класів. Абстрактний базовий клас – це клас, що містить принаймні одну повністю віртуальну функцію-член.

Повністю віртуальна функція-член – це віртуальна функція-член, для якої оголошений інтерфейс, а реалізація перебуває в похідному класі. Для цього потрібно оголосити функцію-член рівною нулю.

 

struct Point

{

int x, y;

Point(int x0 = 0, int y0 = 0){x = x0; y = y0;}

};

 

class Shape

{

protected:

Point center;

public:

Shape(Point c):center(c){};

virtual Shape& Draw() const = 0; // повністю віртуальна

};

 

class Circle: public Shape

{

int radius;

public:

Circle(Point c, int r = 1);

Shape& Draw() const; // Draw() буде визначена

};

 

// ...

Shape amorphus; // помилка: абстрактний клас

Circle c;

Shape* p = &c; // правильно: покажчик на будь-який Shape

 

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

 

class Phone

{

// ...

public:

Phone(int a, int e, int l)

{

// ...

AcceptDigits(); // ця функція викликає ...

}

virtual void GiveDialTone(); // цю функцію ...

long AcceptDigits()

{

// ...

GiveDialTone(); // оскільки прямий виклик перебуває тут

}

// ...

};

 

class PayPhone: public Phone

{

// ...

public:

// ...

void GiveDialTone(); // ця функція не може бути викликана

};

7. Конструктори базових класів

Конструктори базових класів викликаються перед викликом конструкторів похідних класів. Конструктори базових класів повинні бути явно вказані в кожному визначенні конструктора похідного класу, якщо в базового класу немає конструктора за замовчуванням. Ім’я конструктора базового класу і його аргументи, обмежені дужками, відокремлюються від імені конструктора похідного класу двокрапкою. Конструктори множинних базових класів відокремлюються одне від одного комами.

 

Class Phone

{

// ...

public:

Phone(int, int, int); // має аргументи

// ...

};

 

class PayPhone: public Phone

{

// ...

public:

PayPhone(int a, int e, int l): Phone(a, e, l)

{

// ...

}

// ...

};

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

Конструктори базових класів ніколи не повинні прямо чи побічно викликати повністю віртуальні функції-члени.

8. Деструктори базових класів

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

9. Множинні базові класи

Множинні базові класи дозволяють породжувати клас від більш ніж одного класу. Порядок подання базових класів у списку несуттєвий.

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

 

class A{public: void f(); /* ... */};

class B{public: void f(); /* ... */};

class C: public A, public B{/* ... */};

// ...

C c;

c.f(); // помилка: яке f() з A або B?

c.A::f(); // правильно

 

Найбільш зручно вирішувати таку неоднозначність перекриттям у похідному класі обох функцій.

 

class C: public A, public B

{

// ...

public:

void f(){A::f(); B::f();} // перекриваємо обидві функції

// ...

};

 

// ...

C c;

c.f(); // тепер правильно

 

Для класів, породжених від похідних класів із загальною базою, за замовчуванням існує два екземпляри об’єкта загальної бази.

 

struct X{int i; /* ... */ X(int);};

class A: public X{/* ... */ A(int);};

class B: public X{/* ... */ B(int);};

class C: public A, public B{/* ... */ C(int);};

 

До членів загального базового класу можна звернутися через ім’я одного з похідних класів, використовуючи оператор дозволу доступу.

 

C c(0);

++c.i; // помилка: яке i з A або з B?

++c.A::i; // правильно

C* cp = new C(0);

X* xp = cp; // помилка: яке Х?

X* xp = (A*)cp; // правильно

 

 


Лекція 13. Віртуальні базові класи

1. Віртуальні базові класи

Для класів, породжених від похідних класів із загальним віртуальним базовим класом, існує тільки один екземпляр об’єкта загального базового класу.

Віртуальні базові класи оголошуються включенням ключового слова virtual у специфікатор обмеження доступу базового класу.

 

struct V{int i; /* ... */ V(int);};

class A: virtual public V{/* ... */ A(int);};

class B: virtual public V{/* ... */ B(int);};

class C: public A, public B{/* ... */ C(int);};

 

Для доступу до членів загального базового класу не потрібно нічого вказувати додатково, оскільки існує лише один об’єкт цього класу.

Якщо віртуальний базовий клас і похідний клас розділяють ім’я якого-небудь поля, функції-члена або перерахування, то ім’я в похідному класі приховує ім’я у віртуальному базовому класі.

 

struct V{int i; void f();/* ... */};

class A: virtual public V

{

public:

int i;

int f();

/* ... */

};

class B: virtual public V{/* ... */};

class C: public A, public B

{

// ...

void g(){i = f();} // правильно: як A::i так і A::f() приховують

// V::i і V::f()

};

 

Конструктори віртуальних базових класів, якщо такі є, викликаються конструктором останнього класу в ланцюжку похідних класів.

 

V::V(int n):i(n){/* ... */};

A::A(int i):V(i){/* ... */};

B::B(int i):V(i){/* ... */};

C::C(int i):V(i),A(i),B(i){/* ... */};

 

Віртуальні базові класи в оголошенні похідного класу можуть бути змішані з невіртуальними базовими класами; можуть викликати небажані множинні виклики функцій-членів у віртуальному базовому класі.

 

class V

{

public:

void f(){/* ... */}

};

 

class A: virtual public V

{

public:

void f(){/* ... */V::f();}

};

class B: virtual public V

{

public:

void f(){/* ... */V::f();}

};

 

class C: public A, public B

{

public:

void f(){/* ... */A::f(); B::f();} // зауважте, що V::f()викликається

// двічі

};

 

Для подолання зазначеного ефекту можна визначити функцію real_f(), що виконує дії, специфічні для функції f() даного класу. Тоді функція f() буде виконувати послідовні виклики спочатку функції real_f(), а потім функції f().

 

class V

{

protected:

void real_f(){/* ... */}

public:

void f(){real_f();}

};

 

class A: virtual public V

{

protected:

void real_f(){/* ... */}

public:

void f(){real_f();V::f();}

};

 

class B: virtual public V

{

protected:

void real_f(){/* ... */}

public:

void f(){real_f();V::f();}

};

 

class C: public A, public B

{

protected:

void real_f(){/* ... */}

public:

void f(){real_f(); A::real_f(); B::real_f(); V::real_f();}

};

2. Дані-члени

Дані-члени – це набір взаємозалежної інформації, можливо, різних типів, об’єднаної в один об’єкт. Дані-члени можуть перебувати в закритій (private), захищеній (protected) або відкритій (public) частині класу.

Дані-члени можуть мати статичний клас пам’яті (static). Поля, що мають статичний клас пам’яті, спільно використовуються всіма об’єктами класу. До статичних даних-членів класу можливий доступ через ім’я класу (з використанням операції дозволу доступу), а не через конкретний об’єкт класу. Статичні поля повинні бути визначені в області видимості файлу. Вони можуть бути ініціалізовані; якщо ні, то ініціалізуються значенням нуль.

 

//файл Shape.h

 

class Shape

{

static int numShapes; // оголошення

// ...

};

 

// файл Shape.cpp

 

int Shape::numShapes = 0; // визначення

 

Дані-члени можуть бути оголошені як const. Константні дані-члени класу повинні бути ініціалізовані в кожному визначенні конструктора. Імена полів і їхні початкові значення, обмежені дужками, відокремлюються від списку аргументів конструктора двокрапкою.

 

class Phone

{

const int area, exchange, line;

// ...

public:

Phone(int a, int e, int l):area(a), exchange(e), line(l)

{

// ...

}

// ...

};

 

Дані-члени класу можуть бути змінними іншого класу. У цьому випадку потрібна явна ініціалізація, якщо тільки поля не мають конструктора за замовчуванням. Такі поля ініціалізуються тим же способом, що і константні поля.

 

class Phone

{

// ...

public:

Phone(int, int, int); // потрібні аргументи

// ...

};

 

class Car

{

// ...

Phone phone;

public:

Car(/* ... */, int a, int e, int l): phone(a, e, l){/* ... */}

// ...

};

3. Приклад використання статичних членів

#include <cstdio>

 

class Counter

{

public:

static long count;

Counter(){count++;}

long GetInstanceCount(){return count;}

~Counter(){count-і;}

};

 

// визначення і ініціалізація count

long Counter::count = 5;

 

// визначення трьох об’єктів Counter

Counter c1, c2, c3;

 

void main()

{

printf("nThe object count is currently %d", Counter::count);

};

 


Лекція 14. Потокове введення-виведення

1. Класи потоків введення-виведення