Премини към съдържанието
  • Добре дошли!

    Добре дошли в нашите форуми, пълни с полезна информация. Имате проблем с компютъра или телефона си? Публикувайте нова тема и ще намерите решение на всичките си проблеми. Общувайте свободно и открийте безброй нови приятели.

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

     

Архивирана тема

Темата е твърде стара и е архивирана. Не можете да добавяте нови отговори в нея, но винаги можете да публикувате нова тема, в която да продължи дискусията. Регистрирайте се или влезте във вашия профил за да публикувате нова тема.

rusrusrus

Конструктор - как се имплементира и каква е разликата

Препоръчан отговор


Здравейте, имам въпрос относно долния код (който сам по себе си няма смисъл - експериментален е). .
 
#include <iostream>using namespace std; class pets{private:int x, y; public:pets(int a, int b): x(a), y(b){} void printthem(){cout << x << endl << y << endl;} int get_x(){return x;} };   class animal{public:int shkaf;pets masa;animal(int a, int b, int c): shkaf(a), masa(pets(b, c)){}};  int main(){animal jivotno(5, 10, 13);cout << jivotno.masa.get_x();return 0;}
 
Въпросът е как се имплементира 
 
animal(int a, int b, int c): shkaf(a), masa(pets(b, c)){}
 
Това, което разбрах като четях един учебник е, че този конструктор е по-бърз, тъй като masa се създава преди да се изпълни тялото и не се вика defaul конструктора от класа pets. Да, ама в случая има ли defalt constructor след като аз не съм дефинирал такъв и същевременно съм дефинирал конструктор с 2 променливи(т.е. не се ли маха този, defaul конструктор, който компилатора си създава).
След като разбера това ми е интерно и да разбера - има ли смисъл shkaf(a) да е извън тялото? Или е същото като, когато е вътре?
Ами, ако в pets имах един конструктор с 2 параметъра, но и двата имат defaul стойности тогава вика ли се първо този с defaul стойности, ако бях направил конструктор (говоря за този горепосочения - на animal), в който кодът ми е в тялото?
Предполагам после ще се сетя още нещо, но за сега искам да разбера как се имплементира и какво става,а не да назубря да ползвам такъв видя конструктор просто ей така. Искам да знам причината и кога наистина има смисъл от него. Ще се радвам, ако някой ме просветли по темата.
Благодаря предварително.

 

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

1) Default се нарича конструктор, който не приема никакви аргументи(например: animal()). Можеш да имаш няколко на брой конструктори;

2) Компилатора не създава default или каквито и да било конструктори. Ти сам трябва да ги създадеш;

3) Ако променливата "shkaf" е в тялото на класа, то тя важи само за този клас и само този клас може да я използва, а ако е извън класа се нарича глобална променлива, която може да се използва от цялата програма. Както си я дефинирал е само за обекти от класа animal;

4) По този начин (с две точки след конструктора на класа)...

animal(int a, int b, int c): shkaf(a), masa(pets(b, c)){}

... се задават стойности на базов клас, т.е. ако си наследил някой друг клас. Не знам защо на променлива от тип int си написал "shkaf(a)", доколкото знам не би трябвало да става по този начин да присвоиш стойност, но може и да греша.

 

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

class animal{int x, y, z; //Да речем, че тук имаш 3 променливи.public:int shkaf;pets masa;animal(int a, int b, int c): shkaf(a), masa(pets(b, c)) // А тук, когато се създаде обект в main, директно се задават стойности и на базовия клас,                                                        // но това е само ако си наследил някой клас. В твоя случай, тази програма би трябвало да ти изведе грешка.                                                        // В случая това "animal(int a, int b, int c): shkaf(a), masa(pets(b,c))" е грешно, защото нямаш наследен клас.{x=a;y=b;z=c; //Тук можеш да им присвоиш стойности.}};//И примерно в main метода при създаване на обект от тоя клас, задаваш трите стойности.int main(){animal dogg(3, 10, -123); //Тук все едно задаваш стойности на трите променливи x, y, z в класа animal.return 0;}

5) Не знам какво разбираш под default стойности, но обикновено ако не си задал изрично някаква стойност, то функцията приема тези default стойности. Пример:

//........class animal{//...public:animal(int a=2, int b=3){} //};//....int main(){animal dog; // Тук не задаваш никакви стойности, съответно обекта взема тези, които са            // зададени по Default, а те са 2 и 3 в случая.animal cat(0); //Тук когато задаваш само 1 стойност, то само променливата а(на конструктора) се променя, а b си остава 3(по default).animal bird(4, 5); //А тук, както се досещаш и двете се променят. Не можеш да зададеш само на b, а пък 'а' да остане по default.return 0;}

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Това, което разбрах като четях един учебник е, че този конструктор е по-бърз, тъй като masa се създава преди да се изпълни тялото и не се вика defaul конструктора от класа pets.

Първо:

//все едно дали ще напишеш така:animal(int a, int b, int c): shkaf(a), masa(pets(b, c)){// или така:animal(int a, int b, int c): shkaf(a), masa(b, c){

Да, ама в случая има ли defalt constructor след като аз не съм дефинирал такъв и същевременно съм дефинирал конструктор с 2 променливи(т.е. не се ли маха този, defaul конструктор, който компилатора си създава).

След като разбера това ми е интерно и да разбера - има ли смисъл shkaf(a) да е извън тялото? Или е същото като, когато е вътре?

Ами, ако в pets имах един конструктор с 2 параметъра, но и двата имат defaul стойности тогава вика ли се първо този с defaul стойности, ако бях направил конструктор (говоря за този горепосочения - на animal), в който кодът ми е в тялото?

Сега тук има един тънък момент. Така, както си дефинирал класовете, те нямат конструктори по подразбиране. Следователно, и да искаш, няма как да ползваш другия начин. Тоест ако пробваш да иницализираш masa в тялото на конструктора:

animal(int a, int b, int c): shkaf(a){// Първо ще се изпълни списъкът с инициализациите (в случая shkaf(a))// После компилаторът ще се опита използва конструкторите по подразбиране на останалите членове(в случая masa).// Такъв няма и ще получиш грешка при компилация.// и изобщо няма да стигне до "твоята част" от тялото на конструктора:masa = pets(b, c); }
Надявам се, че това отговаря на въпросите ти. Ако инициализираш член променливите в тялото на конструктора, вместо в списъка за инициализация, ще бъдат извикани "излишно" конструктор по подразбиране и евентуално копиращ конструктор. Shkaf е цяло число и няма значение къде ще го инициализираш, но ако беше клас важи предното изречение. Също, да, ако в pets, конструкторът имаше стойности по подразбиране за двата параметъра, той щеше да е конструктор по подразбиране и тогава кода, който аз дадох за пример, щеше да се компилира, и конструкторът щеше да се извика два пъти: веднъж със стойностите по подразбиране и веднъж с тези, с които го инициализираш изрично.

 

1) Default се нарича конструктор, който не приема никакви аргументи(например: animal()). Можеш да имаш няколко на брой конструктори;

2) Компилатора не създава default или каквито и да било конструктори. Ти сам трябва да ги създадеш;

1) Не е точно. Default се нарича конструктор, който може бъде извикан без никакви аргументи. Примерно конструкторът който си показал в 5) е конструктор по подразбиране за класа animal.

2) Това направо не е вярно. Ако за даденият клас не са зададени никакви такива, компилаторът си създава автоматично конструктор по подразбиране и копиращ конструктор, които ще си извика всеки път, когато правиш инстанция на класа или го копираш (явно или не) съответно.

// А тук, когато се създаде обект в main, директно се задават стойности и на базовия клас,// но това е само ако си наследил някой клас. В твоя случай, тази програма би трябвало да ти изведе грешка.// В случая това "animal(int a, int b, int c): shkaf(a), masa(pets(b,c))" е грешно, защото нямаш наследен клас.

също е грешно. Инициализационните списъци (могат да) се използват за всички членове на класа, не само за наследените. Кодът си е съвсем коректен. Даже, както отбелязах, тъй като класът pets няма конструктор по подразбиране, това е единственият начин да се инициализира masa.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

 Инициализационните списъци (могат да) се използват за всички членове на класа, не само за наследените. Кодът си е съвсем коректен. Даже, както отбелязах, тъй като класът pets няма конструктор по подразбиране, това е единственият начин да се инициализира masa.

Благодаря, че ме поправи, не бях видял, че има обект от pets в класа. А integer може ли да се инициализира по този начин("shkaf(a)")? Досега не съм срещал такова нещо.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

А integer може ли да се инициализира по този начин("shkaf(a)")? Досега не съм срещал такова нещо.

Може, във C++ всички вградени типове си имат конструктори по подразбиране и копиращи такива. И изобщо всеки тип задължително има някакъв конструктор и някакъв копиращ конструктор, но за вградените типове стандартът гарантира, че ще работят, докато за тези, които ти си дефинираш, компилатора само ще се опита да направи някаква инициализация, съответно копиране. Твоя е отговорността да си дефинираш коректни, ако е автоматичните няма да ти вършат работа.


Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Благодаря много за отговорите. Това наистина като вкарах masa в тялото не тръгна. Трябва да се сложи default констуктор като например -

masa(){}

А този горният конструктор е същият, който би си сложил компилаторът (когато нямаме никакви констуктори) нали така ? Просто в случаят ми трябва, за да не стане това търсене на несъществуващото (както Вие, flare, казахте) ?

Т.е. този метод (добавянето на такъв вид конструктор ) е приложим, когато използваме инстанция на клас, тъй като тя ще трябва да викне един default конструктор, а после и copy конструктор, а така се вика само един такъв. А има ли някакви други особености свързани с това ? Например да се използва в някакви други ситуации ?

Искам малко да се отклоня от темата и да попитам за inline функциите.На практика ползват ли се те в днешно време? И ако да - кога, къде, ако може примерно кодче ( би трябвало, ако са приложими да са на кратък код така, че мисля, че не би било проблем).

Благодаря ви още веднъж !

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Т.е. този метод (добавянето на такъв вид конструктор ) е приложим, когато използваме инстанция на клас, тъй като тя ще трябва да викне един default конструктор, а после и copy конструктор, а така се вика само един такъв. А има ли някакви други особености свързани с това ? Например да се използва в някакви други ситуации ?Искам малко да се отклоня от темата и да попитам за inline функциите.На практика ползват ли се те в днешно време? И ако да - кога, къде, ако може примерно кодче ( би трябвало, ако са приложими да са на кратък код така, че мисля, че не би било проблем).Благодаря ви още веднъж !

Въпросът ти е малко неясен.Конструктор (независимо по подразбиране или не) трябва да се дефинира само ако е необходим за съответния клас. Това важи и за всички не-частни методи и членове. (обаче конструкторът по подразбиране се ползва в много езикови конструкции, като декларация на масив например, които трябва да се вземат предвид).За копиращ конструктор важи правилото на голямата тройка: Ако класът ти ползва едно от: копиращ конструктор, предефиниран оператор за присвояване или деструктор, то обикновено са ти необходими и другите две. Обикновено това е, когато се ползват ресурси изискващи динамично заделяне и освобождаване (примерно памет).Кога да ползваш inline - това е много сложен въпрос. В зависимост от конкретния случай, декларирането да дадена функция inline може да доведе до по-голям или по-малък, по-бавен или по-бърз код. Също оптимизаторът може да реши да "вгради" функция даже и да не си я декларирал като inline - почти сигурно, ако е статична и се ползва например на едно място или ако е много кратка. Точно това беше и общото правило за деклариране на inline - функции, които се ползват на много малко места в кода или съдържат много кратък код - някаква формула или присвояване - един-два реда. Забележи миналото време. Моят съвет е, да не ползваш inline, освен ако не стигнеш до конкретен проблем, за който мислиш, че може да помогне - и тогава да пробваш. Но не и преди това. Вярвай на оптимизаторите, съвременните компилатори са много напред с материала и личният ми опит показва, че е изключително трудно да постигнеш смислена оптимизация на толкова ниско ниво. Естествено има изключения но те са или за конкретни платформи или за особено специфични случаи.

 

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имах предвид например, ако имаме клас Animal и в него имаме:

 

Animal(){}Animal(int a, int b){...някакъв си код}......int main(){Animal jivotinche;return 0;}

Същото ли ще приеме jivotinche като член-данни както, ако нямаме никакви конструктори ? Това едва ли е важно просто ми стана интересно. Питам, защото в единият аз съм задал експлицитно конструктор(в който обаче няма нищо), а в ругият случай компилаторът ще си създаде такъв.

Благодаря много за бързите отговори.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имах предвид например, ако имаме клас Animal и в него имаме:Същото ли ще приеме jivotinche като член-данни както, ако нямаме никакви конструктори ? Това едва ли е важно просто ми стана интересно. Питам, защото в единият аз съм задал експлицитно конструктор(в който обаче няма нищо), а в ругият случай компилаторът ще си създаде такъв.Благодаря много за бързите отговори.

Ами да, същото - тоест за член променливите ще се извикат конструкторите по подразбиране. Ако са автоматични, примерно числа, то и идея си нямаш, на какво ще са равни. Ако са някакви други - зависи, какво те правят. Ако нямат - ще получиш грешка при компилация.Обаче, в посочения случай не можеш да пропуснеш да дефинираш конструктор по подразбиране, дори и празен да е, защото имаш и друг. Автоматичен се създава, само ако нямаш никакъв дефиниран конструктор.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Да, точно така. Благодаря ви много. ^_^ 

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имам и още един въпрос. Каква е разликата, ако дефинираме една функция вътре в класа и ако само я декларираме вътре,а я дефинираме извън тялото на класа? Бях чел, че ако дефиницията е вътре в тялото и функциите се приемат за inline (освен, ако компилаторът не реши, че не е оптимално да са такива). Като цяло едва ли ще ми потрябва това някога, просто ми е интересно.

Благодаря предварително.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имам и още един въпрос. Каква е разликата, ако дефинираме една функция вътре в класа и ако само я декларираме вътре,а я дефинираме извън тялото на класа? Бях чел, че ако дефиницията е вътре в тялото и функциите се приемат за inline (освен, ако компилаторът не реши, че не е оптимално да са такива). Като цяло едва ли ще ми потрябва това някога, просто ми е интересно.

Благодаря предварително.

От гледна точка на функционалност и генериран код - няма разлика.  Между другото inline важи само ако функцията се ползва в същия файл, т.е. inline функциите по дефиниция са и статични - т.е. публичен метод на публичен клас не можеш да очакваш да бъде inline.

От принципна гледа точка, разликата е гигантска. Нека дефинираме две групи хора. Едните са тези които пишат класа - ще ги кръстя собственици. Другите са тези които употребяват класа - потребители. Винаги пиши клас така все едно друг ще го ползва, не ти.

Декларацията на класа - тоест на неговите методи и членове е публична. Ти я даваш на потребителите на класа. Даже в идеалния случай, ще им дадеш само един абстрактен клас, съдържащ само публичните (или ако потребителите ще правят наследници - и защитеините) методи и членове, който не може да се инстанцира и само служи за интерфейс - да определи какво изисква и какво предоставя твоя клас.

А дефинициите на методите е хубаво да са не просто извън класа, а в отделен файл или даже файлове, Какво има там, е работа само на тези, които разработват класа - авторите. За другите трябва да е черна кутия. Доколкото интерфейса на класа, изискванията и предоставените услуги на класа не се променят, другите не трябва да се интересуват от това какво има в него. Това позволява разработката на приложение да се извършва паралелно след като авторите и потребителите се разберат за интерфейса. Също предпазва потребителите да не се изкушават да се възползват от знанията си за вътрешната имплементация на класа, което води до много неприятни проблеми. Те не трябва да го правят, но все някога ще го направят. Щото повярвай ми - всяко правило, което може да се наруши, в някакъв момент се нарушава. Това важи не само за програмирането.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имам един въпрос относно един от горните ви коментари 

 

 

animal(int a, int b, int c): shkaf(a){
// Първо ще се изпълни списъкът с инициализациите (в случая shkaf(a))
// После компилаторът ще се опита използва конструкторите по подразбиране на останалите членове(в случая masa).
// Такъв няма и ще получиш грешка при компилация.
// и изобщо няма да стигне до "твоята част" от тялото на конструктора:
masa = pets(b, c);
}

Въпросът ми е защо компилаторът ще се пробва да вика конструкторър по подразбиране? Не трябва ли по-скоро да търси оператор '=' ?

Може да е глупав въпросът, но не ми е ясно или пропускам нещо.

Благодаря предварително.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Имам един въпрос относно един от горните ви коментари Въпросът ми е защо компилаторът ще се пробва да вика конструкторър по подразбиране? Не трябва ли по-скоро да търси оператор '=' ?Може да е глупав въпросът, но не ми е ясно или пропускам нещо.Благодаря предварително.

Въпросът не е глупав, това е важно да се разбере.Ами за да може да присвояваш, трябва да имаш готов обект. Присвояването не конструира. Или да го кажа по такъв начин:
// ако masa е някакъв обект.masa = pets(a,b);// е еквивалентно на:masa.operator=(pets(a,b));
Е към момента, в който викаш метод на класа, трябва да имаш инстанция. Така че, преди да започне да изпълнява тялото на конструктора, компилатора трябва да подсигури, че всички член-променливи са конструирани. Тези, които си заявил в инициализиращия списък ще го направят със зададените конструктори, останалите с дефолтните. Затова всяка член променлива трябва да има дефолтен конструктор, ако не е обявена в инициализиращия списък. Ако не ти харесва, трябва да ползваш указатели и да си ги създаваш динамично, с каквито конструктори искаш.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Благодаря за бързият отговор. В main по-същият начин ли търси default constructor, когато имаме

pets masa(a,b)
?

Питам, защото преид малко пробвах на един клас да нямам default constructor,а само copy и компилаторът изкара грешка. Обаче, когато напиша cout << "yeee" в default конструктора(след като го създадох), то никъде не се изпизса "yeeе" - т.е. все едно не се вика. Та ми е интересно защо се викат тези default Конструктори(за този пример, за който питах по-горе) - какво му пречи направо на компилатора да използва копиращия констрктор.

Има ли книга, в която са обяснени тези работи - как работи компилаторът? Естествено, тъй като не съм силен в програмирането, не трябва да е някаква супер задълбочена и със супер много терминология. Питам за такава книга, тъй като в ООП-то доста често почнах да се питам - "Това как се имплементира? Какво става вътре в програмата и какво прави компилатора?". На нас ни казваха, че ООП-то било само синтаксис и т.н., но като цяло според мен е до голяма степен разбиране на това какво става в компилатора.Това е мое мнение, може и далеч да не съм прав.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

Благодаря за бързият отговор. В main по-същият начин ли търси default constructor, когато имаме

pets masa(a,b)
?Питам, защото преид малко пробвах на един клас да нямам default constructor,а само copy и компилаторът изкара грешка. Обаче, когато напиша cout << "yeee" в default конструктора(след като го създадох), то никъде не се изпизса "yeeе" - т.е. все едно не се вика. Та ми е интересно защо се викат тези default Конструктори(за този пример, за който питах по-горе) - какво му пречи направо на компилатора да използва копиращия констрктор.Има ли книга, в която са обяснени тези работи - как работи компилаторът? Естествено, тъй като не съм силен в програмирането, не трябва да е някаква супер задълбочена и със супер много терминология. Питам за такава книга, тъй като в ООП-то доста често почнах да се питам - "Това как се имплементира? Какво става вътре в програмата и какво прави компилатора?". На нас ни казваха, че ООП-то било само синтаксис и т.н., но като цяло според мен е до голяма степен разбиране на това какво става в компилатора.Това е мое мнение, може и далеч да не съм прав.

Защо реши че ще се извика дефолтния конструктор? Този код ще извика конструктор, който приема два параметъра, от такива типове каквито са a и b (или могат да се конвертират до такива) съответно. Това може да е и дефолтния, ако си го дефинирал така, но може и да не е.Това, което си мислиш за ООП не е вярно. В идеалния случай изобщо не трябва да знаеш какво прави компилатора. Но на теб ти липсва солидна част от теорията за езика, не за компилатора. Това кой конструктор, кога се вика го пише във всички смислени учебници.

Сподели този отговор


Линк към този отговор
Сподели в други сайтове

×
×
  • Добави ново...