Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка - Основи Програмування C_.doc
Скачиваний:
46
Добавлен:
18.12.2018
Размер:
1.44 Mб
Скачать

2. Спадкування та правила доступу до членів класів.

З’ясуємо, що відбувається при спадкуванні із закритими (private) членами базового класу. Нагадаємо, що використання закритих членів класу – одна з основ інкапсуляції даних. При спадкуванні закриті члени класу залишаються недоступними для похідних класів. Такий підхід зрозумілий, адже в іншому разі для «зламу» закритих членів класу досить було б утворити похідний від нього клас. Переконайтесь самостійно, що якщо у базовий клас Building включити закрите поле, наприклад, private double cost; то при спробі звернутись до нього із екземпляру похідного класу, вказавши lb.cost, одержимо синтаксичну помилку:

«… is inaccessible due to its protection level» – недоступний із-за його рівня захисту.

Проте, у випадках, коли необхідно дозволити похідним класам використовувати закриті поля базового класу, останні можна визначити із модифікатором доступу protected – захищений. Такий член класу відкритий лише у своїй ієрархії класу і закритий для інших частин програми.

У наступному прикладі в базовому класі Base визначені: захищене (protected) поле field та відкрита властивість Field, яка керує доступом до цього поля (адже у функції Main(), зокрема, неможливо звернутись до поля field безпосередньо). У похідному класі SubBase створимо метод ChangeField(), призначення якого – змінити (в даному прикладі подвоїти) захищене поле field базового класу Base.

using System;

namespace ProtectedClass

{

class Base

{

protected int field;

public int Field

{

get { return field; }

set { field = value; }

}

}

class SubBase : Base

{

public void ChangeField()

{ // Метод має доступ до захищеного поля базового класу

field = 2 * field;

}

}

class Program

{

static void Main()

{

Base b = new Base();

b.Field = 100;

Console.WriteLine("Клас Base: Field = {0}", b.Field);

SubBase sb = new SubBase();

sb.Field = 200;

Console.WriteLine(

"Клас SubBase: Field = {0}", sb.Field);

// Метод змінює захищене поля базового класу

sb.ChangeField();

Console.WriteLine("Клас SubBase пiсля ChangeField() :

Field = {0}", sb.Field);

}

}

}

Після запуску проекту на екрані побачимо наступні повідомлення:

Клас Base: Field = 100

Клас SubBase: Field = 200

Клас SubBase пiсля ChangeField() : Field = 400

Таким чином, похідні класи мають відкритий доступ до захищених членів базового класу. Проте для інших частин коду поле field у екземплярах похідних класів залишається, як і раніше, недоступним.

3. Конструктори базового та похідних класів.

Як відомо, конструктори відіграють важливу роль при створенні об’єктів. Навіть, якщо у класі конструктор не визначений, компілятор створює конструктор за замовчуванням, який присвоює нульові значення всім полям. Важливо зрозуміти, що при створенні екземпляру похідного класу недостатньо роботи лише конструктору цього класу, адже він несе відповідальність тільки за члени похідного класу, який у свою чергу містить всі члени свого базового класу. Отже, необхідно визначити спочатку всі значення полів базового класу. Тому при створенні екземпляру похідного класу компілятором спочатку викликається конструктор базового класу і лише потім конструктор похідного класу. Наступний простий приклад наочно демонструє порядок виклику конструкторів базового та похідних класів.

using System;

namespace InheritanceConstructor

{

class Base // базовий клас

{

public Base()

{

Console.WriteLine(

"Cтворюємо екземпляр базового класу");

}

}

class SubBase : Base // похідний клас

{

public SubBase()

{

Console.WriteLine("Cтворюємо екземпляр похiдного класу");

}

}

class SubSubBase : SubBase // похідний від похідного класу

{

public SubSubBase()

{

Console.WriteLine(

"Cтворюємо екземпляр похiдного вiд похiдного класу");

}

}

class Program

{

static void Main()

{

Base b = new Base();

Console.WriteLine("----------------------");

SubBase sb = new SubBase();

Console.WriteLine("----------------------");

SubSubBase ssb = new SubSubBase();

}

}

}

На екрані одержимо наступні повідомлення:

Cтворюємо екземпляр базового класу

----------------------

Cтворюємо екземпляр базового класу

Cтворюємо екземпляр похiдного класу

----------------------

Cтворюємо екземпляр базового класу

Cтворюємо екземпляр похiдного класу

Cтворюємо екземпляр похiдного вiд похiдного класу

Як бачимо, при створенні екземпляру найнижчого в ієрархії класу послідовно спрацьовують всі конструктори, починаючи від конструктору базового класу. Спробуємо змінити визначення базового класу Base, додавши в нього деяке приватне поле та конструктор з параметром для його ініціалізації:

class Base // базовий клас

{

private int field ;

// конструктор ініціалізує поле field

public Base(int field_)

{

field = field_;

Console.WriteLine(

"Cтворюємо екземпляр базового класу field = {0}", field);

}

}

Спроба скомпілювати цей приклад призведе до помилки «No overload for method 'Base' takes '0' arguments» – немає перевантаженого конструктора 'Base' без аргументів. Поява такого повідомлення компілятора цілком зрозуміла, адже конструктор похідного класу SubBase автоматично намагається викликати конструктор класу Base без параметрів, а клас Base такого конструктору не надає. Можливих виходів із такої ситуації існує декілька. Один із варіантів – створити у класі Base конструктор без параметрів, використавши уже знайомий нам синтаксис виклику одним конструктором класу іншого:

public Base() : this (1) { }

Правда, в цьому випадку прийдеться задовольнитись викликом конструктору із деяким значенням параметру за замовчуванням, в даному прикладі це 1.

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

<специфікатор_доступу> <ідентифікатор_конструктора_похідного_класу>

(<список_параметрів_конструктора_похідного_класу >) :

base (<список_параметрів_конструктора_базового_класу>)

{

// код конструктора похідного класу

}

Внесемо такі зміни у наш приклад.

using System;

namespace InheritanceConstructor

{

class Base // базовий клас

{

private int field ;

// конструктор ініціалізує поле field

public Base(int field_)

{

field = field_;

Console.WriteLine("Cтворюємо екземпляр базового класу

field = {0}", field);

}

}

class SubBase : Base // похідний клас

{

// виклик конструктору базового класу

public SubBase(int field_) : base(field_)

{

Console.WriteLine(

"Cтворюємо екземпляр похiдного класу");

}

}

// похідний клас від похідного класу

class SubSubBase : SubBase

{

public SubSubBase(int field_) : base(field_)

{

Console.WriteLine(

"Cтворюємо екземпляр похiдного вiд похiдного класу");

}

}

class Program

{

static void Main()

{

Base b = new Base(100);

Console.WriteLine("-------------------------");

SubBase sb = new SubBase(200);

Console.WriteLine("-------------------------");

SubSubBase ssb = new SubSubBase(300);

}

}

}

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

Cтворюємо екземпляр базового класу field = 100

-------------------------

Cтворюємо екземпляр базового класу field = 200

Cтворюємо екземпляр похiдного класу

-------------------------

Cтворюємо екземпляр базового класу field = 300

Cтворюємо екземпляр похiдного класу

Cтворюємо екземпляр похiдного вiд похiдного класу

Використання службового слова base в такому контексті дозволяє викликати будь-який із перевантажених конструкторів базового класу відповідно до списку аргументів виклику.

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