Лого на 91. НЕГ „Проф. Константин Гълъбов“

Модул 1 · Урок 18

Конструктори

Конструктор по премълчаване, конструктор с аргументи и копиращ конструктор. Претоварване по сигнатура и класът Rational с истински методи.

Защо ни е това

Досега създавахме обект и после му запълвахме атрибутите ред по ред. Работи, но е досадно и лесно се забравя нещо. Конструкторът позволява обектът да се роди вече готов.

Ключово

Конструкторът е специфичен метод: носи името на класа и се извиква от new. Един клас може да има няколко конструктора — стига да се различават по сигнатура.

Конструктор по премълчаване

Най-простите конструктори вече ги използвахме: функциите Car() и Student(). Тези конструктори, поставени автоматично от средата, само отделят памет за обекта, но не дават на атрибутите стойности, зададени от програмиста.

Всъщност стандартните конструктори задават и стойности по премълчаване — празен низ за променливите от тип string и нулеви стойности за целочислените типове. Ето как изглежда стандартният конструктор на класа Car:

public Car()
{
    this.brand = null;
    this.model = null;
    this.engineVolume = 0;
    this.yearProd = 0;
}

Възможно е сами да предефинираме стандартния конструктор, като поставим по-подходящи стойности по премълчаване. Например вместо празен низ да зададем текст, който подсказва, че на атрибута не е зададена стойност:

public Car()
{
    this.brand = "Не е въведена";
    this.model = "Не е въведен";
    this.engineVolume = 0;
    this.yearProd = 0;
}

Бележка

Ключовата дума this означава „текущата инстанция“. Тя позволява да различиш атрибута на обекта от едноименен параметър — много удобно, когато параметърът се казва точно като атрибута.

Конструктор с аргументи

Сега ще дефинираме алтернативен конструктор, с помощта на който можем да създаваме инстанции на клас, който освен да заделя памет, може и да задава стойности на всички атрибути:

public Car(string b, string m, int e, int y)
{
    this.brand = b;
    this.model = m;
    this.engineVolume = e;
    this.yearProd = y;
}

Създаването на обект вече е един ред:

Без конструктор с аргументиС конструктор с аргументи
СъздаванеCar myCar = new Car();Car myCar = new Car("FlyingCar", "Mark1", 3000, 2010);
ПослеЧетири реда присвояванияНищо повече — обектът е готов
РискЗабравен атрибут остава с нулева стойностКомпилаторът иска всички аргументи

Ако данните идват от конзолата, частта за създаване на инстанцията изглежда така:

string b, m; int e, y;
Console.Write("Въведете марка:"); b = Console.ReadLine();
Console.Write("Въведете модел:"); m = Console.ReadLine();
Console.Write("Въведете обем на двигателя:");
e = int.Parse(Console.ReadLine());
Console.Write("Въведете година на производство:");
y = int.Parse(Console.ReadLine());
Car myCar = new Car(b, m, e, y);

Внимание

Всички конструктори на един клас трябва да са с едно и също име — то е името на класа. Затова, съгласно политиката за претоварване на имената, те трябва да се различават по сигнатурата си — броя и типовете на аргументите.

Не е задължително конструкторът да има аргументи за инициализиране на всички атрибути. Например класът Rational (правилна дроб) може да има два конструктора:

public Rational(int n, int d)
{ num = n; denum = d; }

// Цяло число — представя се като дроб със знаменател 1.
public Rational(int n)
{ num = n; denum = 1; }

Копиращ конструктор

Когато работим с инстанции на един клас, съвсем естествено се налага да присвоим на една инстанция стойностите на атрибутите на друга, както правим това с обикновените променливи. При класове с много атрибути да се извършва такова присвояване за всеки от атрибутите може да е много трудоемко.

Копиращ конструктор

Конструктор, който създава нова инстанция на класа и присвоява на атрибутите ѝ стойностите на атрибутите на съществуваща инстанция.

public Rational(Rational r)
{ this.num = r.num; this.denum = r.denum; }

Сега копирането е един оператор: Rational a = new Rational(x);

Съвет

Точно това позволява задача 1 по-долу: правим четири копия на дробта x и всяко от тях изпълнява различна операция, без оригиналът да пострада.

Задачи

Задача 1 — Rational с методи

Напиши конзолно приложение с класа Rational, което въвежда две дробни числа, пресмята сбора, разликата, произведението и частното им, както и сравнява двете дроби.

static void Main(string[] args)
{
    int xnum, xdenum, ynum, ydenum;
    xnum = int.Parse(Console.ReadLine());
    xdenum = int.Parse(Console.ReadLine());
    ynum = int.Parse(Console.ReadLine());
    ydenum = int.Parse(Console.ReadLine());
    Rational x = new Rational(xnum, xdenum);
    Rational y = new Rational(ynum, ydenum);
    Rational a = new Rational(x); a.Add(y); a.Print();
    Rational s = new Rational(x); s.Sub(y); s.Print();
    Rational m = new Rational(x); m.Mult(y); m.Print();
    Rational d = new Rational(x); d.Div(y); d.Print();
    int c = x.Compare(y);
    if (c == 1) Console.WriteLine("X > Y");
    else if (c == -1) Console.WriteLine("X < Y");
    else Console.WriteLine("X = Y");
}

Забележи колко по-четимо е това от версията с голи формули от урок 8. Формулите не са изчезнали — просто са се преместили в методите на класа.

Упражнения

Задача 2. Създай други разумни конструктори с параметри за класовете Car и Student. Указание: не забравяй, че не може да имаме два едноименни конструктора с еднакви сигнатури.

Задача 3. Създай копиращи конструктори за Car и Student.

Задача 4. Модифицирай конзолните приложения, които вече сме създавали и за които това е възможно, като използваш новите версии на класовете.

Какво трябва да запомниш

  • Конструкторът носи името на класа и няма тип на връщана стойност.
  • Стандартният конструктор дава нулеви стойности; можеш да го предефинираш с по-смислени.
  • Няколко конструктора в един клас се различават само по сигнатура — брой и типове аргументи.
  • Копиращият конструктор приема инстанция на същия клас и пренася атрибутите ѝ.
  • this сочи текущата инстанция и позволява параметърът да се казва като атрибута.