Садржај

Најосновније о класама и објектима

У овој лекцији:

  • Шта је објекат, а шта класа, који је њихов однос,

  • Од чега се састоји класа, шта су поља, методи и својства,

  • Приватни и јавни чланови класе.

Из самог назива објектно оријентисано програмирање је јасно да је централни појам ове парадигме објекат. Зато, пре него што почнемо да се бавимо концептима ООП, потебно је да се укратко упознамо са основним појмовима.

Објекти и класе

Однос између класа и објеката је исти као однос између типова и променљивих. Програмски језици најчешће подржавају више целобројних и реалних типова, логички тип и знаковни тип. Ове типове сматрамо основним типовима и они су део самог језика. Поред основних типова, можемо да користимо и типове које сами дефинишемо. Тако можемо да дефинишемо разне набројевиве типове (Enum) и структуре.

У објектно оријентисаним језицима, класа је у суштини још један од типова које сами дефинишемо када пишемо програм. Након што дефинишемо класу, можемо у програму да користимо примерке (инстанце) те класе, као што користимо променљиве неког целобројног или другог типа. Примерци класе се зову објекти. За креирање објекта одређене класе често се користи израз инстанцирање, што значи стварање инстанце, односно примерка класе.

На основу дефиниције класе компајлер израчунава колико меморије ће бити потребно за сваки појединачан објекат те класе, где ће шта од делова објекта у тој меморији да се налази, на који начин иницијално меморија треба да се попуни и како се касније користи. Посматрајући класе на тај начин, можемо да кажемо да је класа прецизно упутство за касније креирање објеката те класе и извршавање поступака над њима.

Класе су веома сличне структурама. Суштинска разлика између њих је то, што су структуре вредносни типови, а класе су референцирани типови. Подсетимо се шта то значи:

  • када су параметри метода, структуре се (као и променљиве простог типа) преносе по вредности тј. копирају се, док се објекти класа (као и низови и матрице) преносе по референци, тј. методу се прослеђује информација о месту у меморији на коме се налази објекат;

  • за структуре се простор алоцира у статичкој меморији (на стеку позива функција), осим ако су део неког низа или објекта. За објекте се простор увек алоцира у динамичкој меморији тј. на хипу.

Из ове главне разлике између класа и структура проистичу још неке разлике, којима ћемо се бавити у делу посвећеном наслеђивању. До тада можемо да сматрамо да се класе у свему понашају исто као и структуре (изузев када су параметри метода).

Чланови класе

Дефиниција класе садржи прецизан опис свих њених делова. Делови од којих се класа састоји се називају чланови класе. Ти делови (као код структуре) могу да буду поља, својства, методи, операторски методи, индексери и догађаји. У курсу за први разред (у лекцији о структурама) је било речи о пољима и својствима структура. Све што је тамо речено, важи и за поља и својства класа.

Поља

Поља су чланови класе у којима се налазе подаци неког једноставнијег типа (или референце на објекте исте или друге класе, или низове). Поља обично чине неку логичку (смисаону) целину, тј. заједно представљају некакав крупнији ентитет. На пример, класа Prozor која представља положај прозора на екрану, може да изгледа овако:

public class Prozor
{
    public int prviRed;
    public int prvaKolona;
    public int visina;
    public int sirina;
}

У овом примеру сваки објекат класе Prozor можемо да схватимо као правоугаоник у матрици пиксела екрана. Поља класе говоре у ком реду и колони матрице пиксела почиње тај прозор (prviRed, prvaKolona), као и које су димензије тог прозора (visina, sirina).

Реч class у првом реду значи да у наставку наводимо дефиницију класе. Реч public испред речи class значи да је класа доступна у сваком делу пројекта који садржи ову дефиницију. Реч Prozor је име класе. Између заграда { и } налази се тело класе. У телу класе дефинишемо од чега се класа састоји. У конкретном случају, класа Prozor се састоји од четири поља prviRed, prvaKolona, visina, sirina, сва четири типа int.

Реч public испред декларације поља значи да је то поље јавно, тј. да је доступно ван класе, да вредност тог поља може да се чита и мења. Вредности појединих поља објекта се у програму користе навођењем имена објекта, тачке и имена поља, овако: imeObjekta.imePolja.

На пример, за претходно дефинисану класу Prozor, објекте можемо да стварамо и користимо на следећи начин:

Prozor w = new Prozor() { prviRed = 5, prvaKolona = 6, visina = 20, sirina = 50 };
w.visina += 5;
Console.WriteLine("Visina prozora je {0}", w.visina);

Методи

Дефиниција класе може да садржи и функције, које зовемо методи. Методи дефинишу шта можемо да урадимо са објектима дате класе. На пример, у тело класе Prozor можемо да додамо метод Povrsina на следећи начин:

public class Prozor
{
    public int prviRed;
    public int prvaKolona;
    public int visina;
    public int sirina;

    public double Povrsina()
    {
        return visina * sirina;
    }
}

Овај метод израчунава површину прозора, тј. број пиксела у том прозору. Метод користимо на уобичајени начин, као и методе класа из библиотеке:

Prozor w = new Prozor() { prviRed = 5, prvaKolona = 6, visina = 20, sirina = 50 };
...
Console.WriteLine("Povrsina prozora je {0} piksela.", w.Povrsina());

Својства

Својство је члан класе који користи исту синтаксу као поље. То значи да својства класа у програмима користимо као да су то поља (гледајући кôд који само користи поља и својства класе, не можемо да видимо разлику између поља и својства). Међутим, за својства класе се не одваја простор у меморији. Уместо тога, приликом очитавања и додељивања вредности својству, извршавају се наредбе које напишемо у такозваним приступницима (енгл. accessor) датом својству. Свако својство може да има приступник за очитавање вредности који се зове get, и приступник за постављање вредности који се зове set.

Један од ова два приступника може и да се изостави и тада се својство користи само за читање или само за упис вредности (у зависности од тога који приступник је изостављен). На пример, дефиницију класе Prozor можемо да допунимо на следећи начин:

class Prozor
{
    public int prviRed;
    public int prvaKolona;
    public int visina;
    public int sirina;

    public int Povrsina()
    {
        return visina * sirina;
    }

    public int poslednjiRed { get { return prviRed + visina - 1; } }
    public int poslednjaKolona { get { return prvaKolona + sirina - 1; } }
};

Поред раније уведених чланова (четири поља и један метод), додали смо и два својства: poslednjiRed и poslednjaKolona. Оба ова својства имају само приступник get, што значи да могу да се користе само за читање вредности. На пример, можемо да пишемо:

Prozor a = new Prozor { prviRed = 20, prvaKolona = 10, visina = 100, sirina = 200 };
Console.WriteLine(a.poslednjiRed);

али не и

a.poslednjiRed = 200;

или

a.poslednjiRed++;

јер својство poslednjiRed нема приступник set.

Када постоји веза између неких величина које описују објекте класе, као што је случај са класом Prozor и величинама prviRed, visina и poslednjiRed, није добро да за сваку од тих величина користимо поље. Да смо у претходном примеру уместо својства додали поље poslednjiRed, било би компликовано обезбеђивати конзистентност вредности у објекту (могло би се догодити да веза између ових величина грешком буде нарушена). Боље је да се поља користе само за подгрупу тих величина, у којој све величине могу да се мењају независно једна од друге. У нашем примеру независно се мењају prviRed и visina, а величина poslednjiRed се само израчунава и не мења се директно, већ искључиво имплицитно, као последица промене неке од величина које се мењају директно и независно.

Уколико одлучимо да мењање последњег реда (колоне) у ствари значи промену висине (ширине), можемо да уведемо и приступнике set за ова два својства. Тада би дефиниција класе изгледала овако:

class Prozor
{
    public int prviRed;
    public int prvaKolona;
    public int visina;
    public int sirina;

    public int Povrsina()
    {
        return visina * sirina;
    }

    public int poslednjiRed
    {
        get { return prviRed + visina - 1; }
        set { visina = value + 1 - prviRed; }
    }

    public int poslednjaKolona
    {
        get { return prvaKolona + sirina - 1; }
        set { sirina = value + 1 - prvaKolona; }
    }
};

Сада можемо да пишемо и

a.poslednjiRed++;

што би са овако дефинисам приступником set повећало висину прозора за један.

Јавни и приватни чланови класе

У уводном делу је поменуто да је један од разлога за стварање класе била потреба да се доступност неких података и неких функција ограничи. На пример, помоћу класа може једноставно да се постигне да одређени чланови (поља и методи) класе не могу да се користе ван класе којој припадају. За то је довољно да се изостави реч public испред имена поља или метода.

Погледајмо шта би се догодило ако изоставимо реч public испред имена метода Povrsina:

public class Prozor
{
    public int prviRed;
    public int prvaKolona;
    public int visina;
    public int sirina;

    int Povrsina()
    {
        return visina * sirina;
    }
    ...
}

...

Console.WriteLine("Povrsina prozora je {0} piksela.", w.Povrsina());

Приликом покушаја да покренемо програм, добијамо следећу поруку о грешци (подразумева се окружење Visual Studio):

Ово значи да је метод Povrsina недоступан због нивоа заштићености тог метода.

Грешка се односи на линију кода којом желимо да прикажемо површину прозора w. Позивање метода Povrsina у тој линији кода је синтаксна грешка, зато што је та линија ван дефиниције класе Prozor. Наиме, пошто метод Povrsina није означен као јаван, он аутоматски постаје приватан за класу Prozor и ван класе не може да се користи (није доступан).

Потпуно исто важи и за поља класе: изостављањем речи public испред дефиниције тих поља, она постају приватна за класу.

public class Prozor
{
    int prviRed;
    int prvaKolona;
    int visina;
    int sirina;
    ...
}

...

Prozor w = new Prozor() { prviRed = 5, prvaKolona = 6, visina = 20, sirina = 50 };
w.visina += 5;
Console.WriteLine("Visina prozora je {0}", w.visina);

Овога пута добијамо више синтаксних грешака, које се све односе на последње три линије кода у примеру. У тим линијама се приступа вредностима поља w.prviRed, w.prvaKolona, w.visina и w.sirina прозора w, а то због приватности ових поља није дозвољено ван тела класе Prozor.

У случају да програмерима који користе нашу класу желимо да омогућимо да читају вредност поља али не и да је мењају, једноставно и често примењивано решење је да поље оставимо као приватно и да му придружимо ствоство које има само приступник get. На пример:

public class Prozor
{
    int prviRed;
    int prvaKolona;
    int visina;
    int sirina;
    public int PrviRed { get { return prviRed; } }
    public int PrvaKolona { get { return prvaKolona; } }
    public int Visina { get { return visina; } }
    public int Sirina { get { return sirina; } }
    ...
}

Поменимо и да члан класе може и експлицитно да буде проглашен за приватан, писањем речи private уместо речи public у дефиницији тог члана.

public class Prozor
{
    private int prviRed;
    private int prvaKolona;
    ...

Тиме се постиже исти ефекат као када само изоставимо реч public у дефиницији метода. Ми ћемо у примерима ипак експицитно да означавамо приватне делове класе као такве, да бисмо истакли одлуку да ти делови буду приватни. У пракси, тим програмера обично усвоји конвенцију о томе да ли ће код приватних чланова (поља и метода) класе писати реч private или неће. Мада ова одлука не утиче на понашање програма, корисно је да се усвојено правило доследно примењује, јер доприниси разумевању кода са мање напора (због створене навике).

(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+