Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2_OOP_2_sem.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
238.08 Кб
Скачать

Структури і ініціалізація полів

Друге серйозне обмеження пов'язане з процесом створення об'єктів і їх ініціалізацією.

Розглянемо оголошення:

int[] x;

int у = x[0];

Це оголошення коректне. А зараз розглянемо схоже оголошення:

int u;

int v = u;

Це оголошення некоректне, виникне помилка періоду компіляції, що повідомляє, що змінна v не ініціалізувала і не може бути використана в обчисленнях. У чому різниця? Об’єкт x – це масив і, отже, відноситься до посилальних типів. При створенні об'єктів посилального типу всі поля об'єктів ініціалізувалися деякими значеннями. У даному конкретному випадку всі елементи цілочисельного типу набудуть значення по замовчуванню, рівні нулю. Об’єкт u арифметичного типу відноситься до розгорненого типу. Всі поля об'єктів розгорненого типу повинні ініціалізувати явно або стандартним конструктором по замовчуванню у момент створення об'єкту. Різна семантика ініціалізації приводить до наступних обмежень на структури.

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

  • Стандартний конструктор по замовчуванню у структурах є, він в стеку створює об'єкт, ініціалізувавши поля створеного об'єкту значеннями за умовчанням. Цей конструктор не можна замінити власним конструктором без аргументів.

Клас Rational або структура Rational

Повернемося до класу Rational, спроектованого в теоретичних відомостях попередньої лабораторної роботи. Очевидно, що його цілком розумно представити у вигляді структури. Наслідування для нього необов'язкове. Семантика привласнення розгорненого типу більше підходить для раціональних чисел, чим посилальна семантика, адже раціональні числа – це ще один підклас арифметичного класу. Загалом, клас Rational – прямий кандидат в структури. Задамося питанням, наскільки просто оголошення класу перетворити на оголошення структури? Чи досить замінити слово class словом struct? В даному випадку заміною одного слова не обійтися. Потрібно справитися і з іншими вищепереліченими обмеженнями на структури. Зробити це не важко. Приведемо оголошення структури SRational, залишаючи можливість знайти всі відмінності між описами класу Rational і описом структури. Відмітимо, що, слідуючи правилам хорошого стилю, що вимагає давати полям змістовні імена, змінені імена полів.

/// <summary>

/// Структура SRational.

/// визначає новий тип даних - раціональні числа і основні

/// операції над ними - складання, множення, віднімання і ділення.

/// Раціональне число задається парою цілих чисел

/// (numerator,denominator) і зображається

/// зазвичай у вигляді дробу numerator/denominator.

/// Число numerator називається чисельником

/// denominator - знаменником. Для раціонального числа існує

/// безліч його уявлень, наприклад, 1/2, 2/4, 3/6, 6/12.

/// Серед всіх уявлень

/// можна виділити те, в якому чисельник і знаменник взаємно

/// нескорочувані. Такий представник зберігатиметься в полях класу.

/// </summary>

public struct SRational

{

const string NONE_EXIST =

"Не існує раціонального числа " +

"зі знаменником, рівним нулю!";

//Поля класу. Чисельник і знаменник раціонального числа.

int numerator, denominator;

/// <summary>

/// Конструктор класу. Створює раціональне число

/// еквівалентне numerator/denominator

/// але з взаємно нескоротним чисельником і знаменником.

/// Якщо denominator = 0, то викидається виключення

/// що повідомляє про неможливість створити

/// раціональне число із знаменником 0

/// </summary>

/// <param name="numerator">числитель</param>

/// <param name="denominator">знаменатель</param>

public SRational(int numerator, int denominator)

{

if (denominator == 0)

throw new SRationalException(NONE_EXIST);

if (numerator == 0)

{

this.numerator = 0;

this.denominator = 1;

return;

}

//приведення знаку

if (denominator < 0)

{

denominator = -denominator;

numerator = -numerator;

}

//приведення до нескоротного дробу

int m = numerator, n = denominator;

{

int p = 0;

m = Math.Abs(m); n = Math.Abs(n);

do

{

p = m % n; m = n; n = p;

} while (n != 0);

}//Nod

this.numerator = numerator / m;

this.denominator = denominator / m;

}

/// <summary>

/// Представлення раціонального числа

/// у вигляді рядка

/// </summary>

/// <returns>строка у форматі numerator/denominator

/// </returns>

public override string ToString()

{

return numerator + "/" + denominator;

}

public SRational Plus(SRational а)

{

int u, v;

u = numerator * а.denominator + denominator * а.numerator;

v = denominator * а.denominator;

return (new SRational(u, v));

}//Plus

public static SRational operator +(SRational r1, SRational r2)

{

return (r1.Plus(r2));

}

public SRational Minus(SRational а)

{

int u, v;

u = numerator * а.denominator - denominator * а.numerator;

v = denominator * а.denominator;

return (new SRational(u, v));

}//Minus

public static SRational operator -(SRational r1, SRational r2)

{

return (r1.Minus(r2));

}

public SRational Mult(SRational а)

{

int u, v;

u = numerator * а.numerator;

v = denominator * а.denominator;

return (new SRational(u, v));

}//Mult

public static SRational operator *(SRational r1, SRational r2)

{

return (r1.Mult(r2));

}

public SRational Divide(SRational а)

{

int u, v;

u = numerator * а.denominator;

v = denominator * а.numerator;

return (new SRational(u, v));

}//Divide

public static SRational operator /(SRational r1, SRational r2)

{

return (r1.Divide(r2));

}

//Константи класу 0 і 1 - ZERO і ONE

public static readonly SRational ZERO, ONE;

SRational(int num, int den, string t)

{

numerator = num; denominator = den;

}//Закритий конструктор

static SRational()

{

ZERO = new SRational(0, 1, "");

ONE = new SRational(1, 1, "");

}//Статичний конструктор

//Операції відношення

public static bool operator ==(SRational r1, SRational r2)

{

return ((r1.numerator == r2.numerator) &&

(r1.denominator == r2.denominator));

}

public static bool operator !=(SRational r1, SRational r2)

{

return ((r1.numerator != r2.numerator) ||

(r1.denominator != r2.denominator));

}

public static bool operator <(SRational r1, SRational r2)

{

return (r1.numerator * r2.denominator <

r2.numerator * r1.denominator);

}

public static bool operator >(SRational r1, SRational r2)

{

return (r1.numerator * r2.denominator >

r2.numerator * r1.denominator);

}

public static bool operator <(SRational r1, double r2)

{

return ((double)r1.numerator / r1.denominator < r2);

}

public static bool operator >(SRational r1, double r2)

{

return ((double)r1.numerator / r1.denominator > r2);

}

public override bool Equals(object obj)

{

return this == (SRational)obj;

}

public override int GetHashCode()

{

return numerator + denominator;

}

}

/// <summary>

/// Клас, що задає виключення при роботі

/// з раціональними числами.

/// </summary>

public class SRationalException:Exception

{

public SRationalException() { }

public SRationalException(string message) : base(message) { }

public SRationalException(string message, Exception e) : base(message, e) { }

}

Всі раніше побудовані приклади роботи з класом Rational застосовні і при роботі із структурою SRational і даватимуть еквівалентні результати за одним виключенням. При виклику конструктора без аргументів для класу Rational створюється коректне раціональне число 0/1. Для структури конструктор за умовчанням створює некоректне число 0/0. Цю ситуацію ніяк не можна виправити, оскільки цей конструктор не можна перевизначити. Єдиний вихід – не користуватися цим конструктором при роботі із структурами.

Побудуємо приклад роботи з об'єктами структури SRational:

public void TwoSemantics()

{

SRational sr1 = new SRational(1, 3);

SRational sr2 = new SRational(3, 5);

SRational sr3, sr4;

sr3 = sr1 + sr2; sr4 = sr3;

if (sr3 > 1) sr3 = sr1 + sr3 + SRational.ONE;

else sr3 = sr2 + sr3 - SRational.ONE;

Console.WriteLine("Структура SRational");

Console.WriteLine("sr1 = " + sr1.ToString());

Console.WriteLine("sr2 = " + sr2.ToString());

Console.WriteLine("sr3 = " + sr3.ToString());

Console.WriteLine("sr4 = " + sr4.ToString());

}

В даному прикладі використовуються константи, працює статичний конструктор, закритий конструктор, перевантажені операції порівняння, арифметичні вирази над раціональними числами. В результаті обчислень r3 набуде значення 8/15, r4 - 14/15. Відмітьте, аналогічний приклад для класу Rational дасть ті ж результати. Для класу Rational і структури Rational не можна виявити різницю між посилальним і розгорненим привласненням. Це пов'язано з особливістю класу Rational – він за побудовою відноситься до незмінних (immutable) класів, аналогічно класу string. Операції цього класу не змінюють поля об'єкту, а кожного разу створюють новий об'єкт. В цьому випадку можна вважати, що об'єкти класу володіють привласненням розгорненого типу.

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