Loading...
MartinBG avatar MartinBG 4803 Точки

Домашно [06. Full C++ OOP] - Въпроси и коментари

Тема за въпроси, свързани с домашното от 6-та лекция - Full C++ OOP, както и за коментари по задачите.

 

Започвам с въпроси относно 3-та задача:

 

1. "Write a SequencePrinter class, which has a pure-virtual print() method and a pure-virtual setSequence(const SequenceGenerator& sequence) method. Derive a SequencePrinterToString, SequencePrinterToFile, SequencePrinterToConsole class. Any class implementing setSequence(const SequenceGenerator& sequence) should change the sequence the current object work with, with the sequence passed-in from the method."

Условието предполага, че setSequence метода тябва да бъде имлементиран във всяко от трите разклонения на класа SequencePrinter. Какво различно се очаква да прави този метод във всеки от тези класове? 

Ето как изглежда тази част в моята имплементация, като setSequence е идентичен във всички разлонения и е по-скоро излишен в този си вид (по-добре да не е виртуален, а дефиниран и имплементиран в SequencePrinter). changeSequence метода го добавих само за да мога да имплементирам setSequence като pure virtual, т.е. също би бил излишен при другото решение. Явно пропускам нещо.

 

2. Какво се очаква да прави print() метода в SequencePrinterToString класа?

Тагове:
1
C++ Programming 08/04/2017 15:59:07
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Здравей,

1. Да, за тази задача setSequence на практика прави едно и също нещо за 3-те класа, които трябва да създадете. Идеята е SequencePrinter да е "ооп интерфейс", затова в него всички методи са pure-virtual. Тоест, представи си, че ти дават код, в който го има този интерфейс и ти дават задача да направиш 3 класа, които следват (имплементират) този интерфейс - много често ще се случва да имаш pure-virtual interface и да ти трябва да имплементираш няколко класа, които го наследяват.

Ти правилно забелязваш, че 3-те класа, които трябва да създадеш имат общи неща и имат различни неща. Какво правим когато имаме едно и също поведение между няколко различни класа :) ? (hint: никъде не е казано, че е задължително 3-те класа директно да наследяват SequencePrinter)

2. "The SequencePrinterToString should be able to print a sequence to a string (by appending)", тоест трябва да конкатенираш стринга с sequence-а, който се намира в обекта ти (+=, или чрез stringstream <<)

Поздрави,

Жоро

3
MartinBG avatar MartinBG 4803 Точки

Благодаря за отговора! :)

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

От коментара ти разбирам, че може би по-добро решение ще е SequencePrinterToString да наследява директно SequencePrinter, като има вариант и да се "сприятелят" частично или напълно, а SequencePrinterToFile и SequencePrinterToConsole да наследяват SequencePrinterToString (и без друго в настоящата ми имплементация те го използват за създаване на стринга, който пък още по-добре ще е да се рализира с << operator overload). Ако ми остане време от другите задачи, може да се върна и да я преработя. 

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

1
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

В тази задача има нещо като уловка и тя е именно този const SequenceGenerator & параметър. Ако полето ти в класа е референция, няма как да имаш setSequence() метод, защото след като една референция е инициализиране е невъзможно да я накараш да сочи към нещо друго - ако кажеш setSequence(someOtherGenerator), това няма да смени твоя printer да ползва друг generator, a просто ще презапише върху предишния generator този, който подаваш (т.е. все едно извън класа си присвоил на единия генератор другия).

Това, което аз бих направил в този случай, е да си направя полето в класа да е const SequenceGenerator *, вместо референция - понеже е pointer, мога да го карам да сочи към друго място, тоест мога правилно да имплементирам setSequence(). Също така, понеже имам pure-virtual клас, който трябва да наследя с няколко класа, които имат общо помежду си, ще си направя един общ базов клас за тях - SequencePrinterBase примерно:

class SequenceGeneratorBase : public SequenceGenerator 
{
protected:
    const SequenceGenetarot* m_sequence;
    SequenceGeneratorBase() :
        m_sequence(nullptr) {}
public: 
    virtual void setSequence(const SequenceGenerator& sequence)
    {
        this->m_sequence = &sequence;
    }
}

Оттук нататък наследяваш SequenceGeneratorBase с класовете дадени в задачата, а метода го викаш ето така:

SequenceGenerator * sequence = new SqrtGenerator(...);
...
SequencePrinter * printer = new SequencePrinterToString(...);
printer->setSequence(*sequence);

Но защо тогава да е референция, а не просто указател - само за да е уловка ли? Не. Понякога се ползва референция, за да е по-трудно да се допусне грешка с параметъра - ако е указател, лесно могат да ти подадат указател към произволна памет, cast-нат към съответния тип директно като викат метода. Когато е референция, на викащия му се налага поне да има някаква променлива предварително от някакъв тип. Разбира се, в C++ винаги може да ти подадат грешен параметър, но с този код ако някой неволно сгреши докато пише е малко по-вероятно да го хване компилатора. Не е напълно солиден подход и не се ползва много, но исках да ви срещна и с тази ситуация, защото в истинската работа би могло да ви се случи нещо подобно.

Поздрави,

Жоро

2
09/04/2017 16:04:47
MartinBG avatar MartinBG 4803 Точки

Благодаря за изчерпателния отговор!

С него плюс още едно преглеждане на видеото и демата от последната лекция, нещата почнаха да ми се нареждат.

Поздрави!

0
Wanker avatar Wanker 15 Точки

Hello,

върнах се на тази задача и по-точно на класа SequencePrinterToString, защото хич не ми харесва как съм го разбрал и имплементирал.

Въпросът ми е трябва ли задължително да override-неме print() функцията за SequencePrinterToString или може да overload-неме += или << и да append-ваме с тях върху някой стринг.

В моя случай (https://pastebin.com/ZRkp1kUR) имам референтнo стринг поле в класа, което инициализираме в конструктора, и с print() append-вам към стринга, но това ми се струва неправилно, защото всяка нова инстанция, конструирана с един и същ стринг, ще достъпва една и съща памет.

Ще се радвам на малко помощ.

0
12/04/2017 19:15:00
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Здравей,

По-скоро е задължително да имплементираш print() - замисли се за варианта с конзолата. Ако имаш няколко SequencePrinterToConsole, те всичките принтират към една и съща конзола, не към различни конзоли, нали? Ако са няколко SequencePrinterToFile, сочещи към един и същи файл, ще пишат по един и същи файл. Е за string-а е същото, все към един и същи string принтираш ако направиш няколко инстанции, които сочат към него. Като цяло идеята на задачата е да свикнете да работите с класове, които имат достъп до обекти от друг клас в себе си

Ето една валидна ситуация, в която можеш да го ползваш - искаш да имплементираш функцията на google търсачката, която като ѝ напишеш "first 5 prime numbers" ти ги извежда (то в момента просто ти извежда най-related резултата, но да речем, че има нещо което го смята директно). Обаче искаш да работи и за sqrt, и за fibonacci, и т.н. - е тогава нещото към което печаташ е все едно и също, но си подготвил няколко различни принтера и пускаш този с правилната заредена sequence (за да може ако потребителя реши да смени нещото, което гледа, да можеш директно да извикаш някоя от другите инстанции и да му покажеш друг sequence, вместо да ги изчисляваш наново). Разбира се има и други начини да го постигнеш това, но в случая се иска да имплементираш нещо, което работи така. 

Ако искаш да си поиграеш с operator overloading, това което бих ти препоръчал е да напишеш operator<<(const SequenceGenerator& sequence), вътре в SequencePrinter (тоест SequencePrinter << SequenceGenerator). Като се изпълни този оператор, принтера ти взема sequence-а и го принтира директно в целта си, каквато е тя (за да можеш с един ред код да смениш sequence-а и да го изпечаташ, вместо да трябва първо да правиш setSequence и след това да викаш print())

Поздрави,

Жоро

0
Wanker avatar Wanker 15 Точки

Мерси за отговора. :)

0
fantom4e avatar fantom4e 24 Точки

Здравейте, не бях сигорен дали трябва да отварям нова тема по въпроса за това го сложих тука.
Някой  да има проблем с проверяването на домашните от  лекцията за Фул ООП. Някои от домашните тръгват на Коде-Блокс, някои на Вижуал Студио,трети пък не тръгват на нито едно от двете.Дори моята задача 04. шах, която съм я писъл на Коде-Блокс  не  тръгва на Вижуал Студио.

0
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Здравей,

Какво имаш предвид, че не тръгва? 

Принципно домашните е достатъчно да съдържат само .h и .cpp файлове (т.е. самия код). Оттам нататък тези файлове дали ще ги Add-нете във Visual Studio, в Code:Blocks, в Qt, в CLion или ще ги компилирате на ръка от конзолата, няма значение. Виж на края на лекцията за Code Organization как взехме SmartArray класа и го компилирахме под няколко различни IDE-та от споменатите.

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

Поздрави,

Жоро

0
fantom4e avatar fantom4e 24 Точки

Ох.. прав си,всичко тръгва като изполвам само хедер и соурс файловете.Извинявам се за моето невнимание и отново неуместен въпрос.
Поздрави!

0
georgi.stef.georgiev avatar georgi.stef.georgiev 921 Точки

Е, няма проблем, нали затова е форумът.

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

Но това не означава, че не може да питаш като видиш, че губиш твърде много време върху нещо.

0
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.