Uceni na zkousku z objektove orientovaneho programovani, jak uz vetsina studentu objevila, by melo obsahova dve casti: strucne se zorientovat v problematice programovani v c++ a vyzkouset si test z predchozich let. Ukazky z testu jsou na forech, ovsem nektera zadani jsou tam nepresne a vyjimecne se vyskytne i chybna odpoved. Zkusila jsem tedy pomoci materialu z tohoto fora a z me zkousky sestavit tento dokument.
Presto, ze jsem se snazila, aby odpovedi zde byly spravne, mohla se mi vloudit chyba. Kdybyste nejakou objevili, dejte mi prosim vedet :)
a jestli zkousku neudelate, nesvalujte vinu na me :)
Toto je vytah otazek z testu z predchozich let - cerpala jsem ze sve zkousky, z prispevku na foru http://mff.fear.cz/forum/viewforum.php?f=25 a ze slidu z prednasek. Dekuji prednasejicim i spoluzakum, kteri se sverili s obsahem svych testu :)
Me velike diky patri taky tem, co mi pisi k tomuto webu komentare, poznamky, opravuji chyby a doplnuji informace.
Pro zvladnuti skutecneho testu by melo postacit pochopit tyto vzorove otazky :)
Za pomoc s pripravou na mou zkousku dekuji vsem, co do fora prispeli svymi testy a co mi poradili, kdyz jsem neco nechapala.
Test je delany zaskrtavaci formou, spravne muze byt jedna a vice odpovedi. Ovsem pozor - trestne body se pocitaji jako rozdil tvych a spravnych odpovedi. Tedy napriklad spravne je moznost A a ty zaskrtnes B, pak mas hned dva body dolu (prvni chyba je nezaskrtnute A a druha chyba je zaskrtnute B). Proto se obcas vyplati strategie - kdyz si nejsem jista, nezaskrtnu nic :)
Testy nejsou studentum vraceny zpet, takze nektere varianty odpovedi se nezachovaly - zverejny jsou jen ty spravne, ktere si nekdo zapamatoval :)
Znamkovani bylo takove:
0 - 2 chyby: 1
3 - 5 chyb: 2
6 - 8 chyb: 3
A jeste jedna dobra rada: poradne si precteti zadani! Pisemka je plna mist, kde lze lehce neco duleziteho prehledout a na pozdejsi reklamace nebude bran zretel :)
co je to singleton?
odpoved:
trida, ktera ma pouze jednu instanci
Predpokladejme tyto deklarace:
class T
{
public:
virtual void f();
}
class U: public T
{
public:
virtual void f();
}
U x;
T y=x;
y.f()
odpoved:
T::f()
Predpokladejme tyto deklarace:
class Base
{
public:
virtual Base operator+ (const Base &) const;
}
class Complex;
class Real:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Real operator+ (const Real &) const;
virtual Complex operator+ (const Complex &) const;
}
class Complex:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Real operator+ (const Real &) const;
virtual Complex operator+ (const Complex &) const;
}
Real r;
Complex c;
Base &b = r;
b = b + c;
[A] Real::operator+ (const Base &) const
[B] Complex::operator+ (const Complex &) const
[C] Complex::operator+ (const Base &) const
[D] Complex::operator+ (const Real &) const
odpoved:
A
(protoze pred b je v deklaraci &, je odkaz na cast objektu typu real, proto se pouzije funkce definovana ve tride real.)
Kdy se vola konstruktor prvku dynamicky alokovaneho pole?
[A] tesne pred prvnim pristupem k prvku
[B] na prvky pole sa nevolaji konstruktory
[C] prvky pole nesmi mit konstruktory
[D] automaticky po naalokovani pole
odpoved:
D
namespace T
{
double f(char x);
};
namespace U
{
char f(double d);
};
int f(int i);
using namespace T;
using namespace U;
char x = f('A');
[A] T::f(char)
[B] U::f(int)
[C] nelegalni volani
[D] U::f(double)
odpoved:
A
(pri pretezovani funkci se vybere ta funkce, pro kterou je konverze nejlevnejsi. tady je odpoved zrejma - pro char ani nemusime konverzi delat.)
class T
{
public:
T();
T(const T &);
T(int);
}
void f(const T & p) {}
[A] T::T()odpoved:
[B] T::T(const T &)
[C] zadnym
[D] T::T(int)
C
(predavame odkaz - zadny objekt nevznika ani se nekopiruje)
otazka:
Ktera metoda neni vhodna pro tridu T?
[A] T & operator= (T &) constodpoved:
[B] T()
[C] T & operator= (const T &)
[D] T(const T &)
A
(operator = by nemel byt konstantni, aby mohl prirazovat hodnotu)
templatestruct T
{
enum { v = 1 };
}
templatestruct T
{
enum { v = 2 };
}
templatestruct T
{
enum { v = 3 };
}
char a[ T::v];
[A] neni definovane
[B] 2
[C] 3
[D] 1
odpoved:
C
(jestlize alespon jeden typ je presne urcen, ma tato definice prednost)
class SStk
{
public:
/* ... */
void push(const std::string & s);
private:
std::string * p_;
int allocated_, used_;
};
void SStk::push (const std::string & s)
{
[1] if (used_ >= allocated)
[2] {
[3] int n2 = (allocated_ +1) * 2;
[4] std::string * p2 = new string[n2];
[5] for(int i = 0; i < used_; ++i)
[6] p2[i] = p_[i];
[7] delete[] p_;
[8] p_ = p2;
[9] allocated_ = n2;
[a] }
[b] p_[used_] = s;
[c] ++used_;
}
odpoved:
4, 6, b
(4 - pro nedostatek pameti. 6 - to, ze jsme zvetsili alokovanou pamet dvakrat jeste neznamena, ze je vetsi nez used; navic opet muze spadnout pro nedostatek pameti (volani copy konstruktoru operatorem =, tedy alokace pameti). b - vubec nezkoumame velikost s; opet alokujeme pamet v copy konstruktoru)
otazka:
Pokud nastane vyjimka:
[A] zanecha zasobnik v nezmenenem stavu
[B] muze zpusobit ztratu (nedostupnost) alokovane pameti
[C] muze zanechat zasobnik v nekonzistentnim stavu, ktery muze zpusobit behovou chybu
odpoved:
B
(B - Nekonzistentni stav je takovy stav, ve kterem je cast prvku v zasobniku v poradku (tedy platne ukazatele) a cast ukazatelu ukazuje na nejake nahodne misto v pameti. A ted proc se zasobnik nemuze dostat do nekonzistentniho stavu? Cely trik je skryty v radku [8]. Kdyz vznikne vyjimka na radku [6], nachazi se pole p2 v nekonzistentnim stavu ... avsak radek [8] se uz nevykona. To znamena, ze v zasobniku zustanou stare prvky p_ , ktere jsou konzistentni. -> zaslal Peto Cerno)
Kdy se vyhodnoti konstruktor globalni promenne?
odpoved:
pred vstupem do main
v jakem poradi se volaji konstruktory tridy? (datove polozky, predci tridy, trida)
odpoved:
predci tridy, datove polozky, trida samotna
v jakem poradi se volaji destruktory tridy? (datove polozky, predci tridy, trida)
odpoved:
trida samotna, datove polozky, predci tridy
(v opacnem, nez kostruktory)
ktera z techto funkci se nejvic hodi pro implementaci scitani dvou komplexnich cisel:
[A] Complex operator+(Complex a, Complex b);
[B] Complex& operator+(const Complex& a,const Complex& b);
[C] Complex operator+(Complex& a, Complex& b)const;
[D] Complex operator+(const Complex& a, const Complex& b);
odpoved:
D
(vhodne je vracet hodnotou, globalni funkce by nemela byt const, argumenty by mely byt const, aby funkce mohla zpracovavat i konstantni argumenty)
class T
{
public:
double f(char c)
{
printf("double char");
return c;
}
};
class U:public T
{
int f(int i)
{
printf("int int");
return i;
}
public:
char f(double d)
{
printf("char double");
return (int)d;
}
};
void main (void)
{
U z;
char x = z.f('A');
}
odpoved:
tento kod skonci s chybou "cannot acces private member declared in class 'U'"
(pri pretezovani funkci se neberou v uvahu funkce zdedene, nejlevnejsi konverze z char je na int, ale ta je privatni)
Jaky musi mit destruktor abstraktni trida?
[A] virtualni
[B] ciste virtualni
[C] zadny
odpoved:
A
Jaky je rozdil?
class T1
{
public:
int a;
void f();
}
struct T2
{
int a;
void f();
}
[A] T1 nelze prirazovat
[B] T2 nelze prirazovat
[C] zadny
odpoved:
C
(struct je synonymum pro class-public)
class base
{
public:
virtual base operator+(base &);
}
class real: public base
{
public:
virtual base operator+(base &);
real operator+(real &);
}
class complex: public base
{
public:
virtual base operator+(base &);
complex operator+(complex &);
}
real r;
complex c;
base b=r;
b=b+c;
[A] complex complex::operator+(complex &);
[B] real real::operator+(real &);
[C] base complex::operator+(base &);
[D] base base::operator+(base &);
odpoved:
D
(b je typu base, protoze to neni odkaz, ani ukazatel)
class Complex
{
public:
double Re, Im;
Complex(Re=0.0, Im=0.0);
Complex operator+(Complex &);
}
Complex Complex::operator+(Complex & b)
{
double r = Re + b.Re;
double i = Im + b.Im;
???;
}
[A] return Complex(r) + Complex(i);
[B] return * this;
[C] return Complex(r, i);
[D] neni legalni
odpoved:
C
class Complex
{
public:
double Re, Im;
Complex(Re=0.0, Im=0.0);
Complex operator+(Complex &) const;
}
Complex Complex::operator+(Complex & b) const
{
Re = Re + b.Re;
Im = Im + b.Im;
???;
}
[A] return Complex(r) + Complex(i);
[B] return * this;
[C] return Complex(r, i);
[D] neni legalni
odpoved:
D
(funkce je konstantni, nemuze menit data v objektu)
class T
{
T();
T(int);
T(const T &);
}
[A] T::T();
[B] T::T(int);
[C] T::(const T&);
odpoved:
C
(pri predavani hodnoty do funkce se objekt kopiruje, proto copy-konstruktor)
f(std::vectorv)
{
[A] std::vector::iterator it;
[B] it=std::find(v.begin(), v.end(), 0);
[C] v.insert(it, -1);
[D] v.insert(it+1, 1);
}
odpoved:
D
(po insertu se vsechny iteratory zneplatni)
class A
{
public:
virtual f();
}
class B: public A
{
public:
virtual f();
}
B b;
A a=b;
a.f();
[A] A::f()
[B] B::f()
odpoved:
A
(virtualni funkce se v potomcich redefinuje. zavola se ta, ktera patri k 'volajicimu' objektu.)
na kterych radcich muze nastat vyjimka?
f(std::vectorv)
{
[A] std::vector::iterator it;
[B] it=std::find(v.begin(), v.end(), 0);
[C] v.insert(it+1, 1);
[D] v.insert(it, -1);
}
odpoved:
C, D
(C - pokud bychom nulovy prvek nenasli, D - pokud by C proslo, po insertu jsou iteratory neplatne)
kdy se vykonaji konstruktory nestatickych polozek objektu?
[A] pri dynamicke alokaci
[B] po skonceni konstruktoru
[C] az je potreba
[D] pred telem konstruktoru objektu
odpoved:
D
(pozor - nestaticke polozky neznamenaji dynamicke, ale polozky, ktere nejsou uvozene slovem static)
jaky by mel byt konstruktor abstraktni tridy?
[A] private
[B] public
[C] protected
[D] zadny
odpoved:
C
Class U
{
U() {a = 1}
U(int i) {a= i + 2}
int a;
}
Class T: public U
{
T() : U(2) {}
T(int i) {a = a + 3}
T(const U &p) : U(p.a) {}
}
T x = U();
[A] 4
[B] 3
[C] 5
[D] 1
odpoved:
B
(nejdriv se zavola U(), takze a=1. potom konverzni konstruktor z T na U)
co je konzistentni stav?
odpoved:
- nedostupna data byla korektne destruovana a odalokovana
- ukazatele nemiri na odalokovana data
- plati dalsi invarianty dane logikou aplikace
jaky je nejvhodnejsi operator+= pro Complex
[A] Complex operator+=(const & Complex)
[B] Complex& operator+=(const & Complex)
[C] Complex& operator+=(const & Complex, const & Complex)
[D] Complex& operator+=(const & Complex, const & Complex) const
odpoved:
B
trida SStk implementuje zasobnik retezcu takto:
class SStk
{
public:
/* ... */
void pop(std::string & out);
private:
std::string * p_;
int allocated_, used_;
}
void SStk::pop(std::string & out)
{
[a] if(used_)
{
[b] --used_;
[c] out=p_[used_];
}
}
[A] zanecha zasobnik v nezmenenem stavu
[B] zanecha zasobnik ve zmenenem, avsak konzistennim stavu
[C] zanecha zasobnik v nekonzistentnim stavu
odpoved:
c, B
(c - muze dojit pamet, B - used_ se jiz odecetlo, coz je spravne. pri vyjimce se nic nevrati, ale stav zasobniku bude zmeneny a v podstate stejny, jako by k vyjimce nedoslo)
predpokladejme tyto deklarace:
class T
{
public:
int a;
};
class U : public T
{
public:
int b;
};
T x;
U y;
x=y;
[A] dojde k preteceni dynamicky alokovane pameti
[B] cast objektu y se okopiruje na misto objektu x
[C] prikaz bude kompilatorem oznacen jako chybny
[D] objekt x zanikne a misto nej vznikne kopie y
odpoved:
B
ktery z techto globalnich operatoru je nejvhodnejsi jako implementace scitani komplexnich cisel?
[A] Complex operator+ (Complex &, Complex &)
[B] Complex operator+ (const Complex &, const Complex &)
[C] Complex operator+ (const Complex &, const Complex &) const
[D] const Complex operator+ (const Complex &, const Complex &)
odpoved:
B
(A - kdyby byly argumenty const, tak je nevezme. C,D - ty const z toho delaji nepouzitelne funkce - nebo hodne tezko pouzitelne)
trida SStk implementuje zasobnik retezcu takto:
class SStk
{
public:
/* ... */
void pop(std::string & out);
private:
std::string * p_;
int allocated_, used_;
}
void SStk::pop(std::string & out)
{
[a] if(used_)
{
[b] out=p_[used_-1];
[c] --used_;
}
}
[A] zanecha zasobnik v nezmenenem stavu
[B] zanecha zasobnik ve zmenenem, avsak konzistennim stavu
[C] zanecha zasobnik v nekonzistentnim stavu
odpoved:
b, A
(b - muze dojit pamet)
Predpokladejme tyto deklarace:
class Base
{
public:
virtual Base operator+ (const Base &) const;
}
class Complex;
class Real:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Real operator+ (const Real &) const;
virtual Complex operator+ (const Complex &) const;
}
class Complex:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Complex operator+ (const Complex &) const;
virtual Complex operator+ (const Real &) const;
}
Real r;
Complex c;
Base b = r;
b = b + c;
[A] Real::operator+ (const Real &) const
[B] Real::operator+ (const Base &) const
[C] Complex::operator+ (const Real &) const
[D] Base::operator+ (const Base &) const
odpoved:
D
(b je typu base)
predpokladejme tyto deklarace:
class T
{
public:
int a;
};
class U : public T
{
public:
int b;
};
U x;
T y;
x=y;
[A] prikaz bude kompilatorem oznacen jako chybny
[B] objekt x zanikne a misto nej vznikne kopie y
[C] promenna x se presmeruje na objekt y
[D] okopiruje cely objekt y na misto objektu x
odpoved:
A
(nemuzeme 'mensi' (rodice) objekt nakopirovat do 'vetsiho' (potomka))
(kod je nesmyslny - to neni omyl. skutecne existuje varianta pisemky, ve ktere se vyskytuje)
trida SStk implementuje zasobnik retezcu takto:
class SStk
{
public:
/* ... */
void push(const std::string & s);
private:
std::string * p_;
int allocated_, used_;
};
void SStk::push (const std::string & s)
{
[1] if (used_ >= allocated)
[2] {
[3] int n2 = (allocated_ +1) * 2;
[4] std::string * p2 = new string[n2];
[5] for(int i = 0; i < used_; i++)
[6] p_[i] = p2[i];
[7] delete[] p2;
[8] p_ = p2;
[9] allocated_ = n2;
[a] }
[b] p_[used_] = s;
[c] ++used_;
}
[A] zanecha zasobnik v nezmenenem stavu
[B] muze zpusobit ztratu (nedostupnost) alokovane pameti
[C] muze zanechat zasobnik ve stavu neodpovidajicim uvedenemu konzistentnimu stavu
odpoved:
4, 6, b, B, C
(4 - pro nedostatek pameti. 6 - to, ze jsme zvetsili alokovanou pamet dvakrat jeste neznamena, ze je vetsi nez used; navic opet muze spadnout pro nedostatek pameti (volani copy konstruktoru operatorem =, tedy alokace pameti). b - vubec nezkoumame velikost s; opet alokujeme pamet v copy konstruktoru; B - ztratime nove alokovanou pamet; C - kvuli te ztrate pameti)
Predpokladejme tyto deklarace:
class Base
{
public:
virtual Base operator+ (const Base &) const;
}
class Complex;
class Real:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Real operator+ (const Real &) const;
virtual Complex operator+ (const Complex &) const;
}
class Complex:public Base
{
public:
virtual Base operator+ (const Base &) const;
virtual Complex operator+ (const Complex &) const;
virtual Complex operator+ (const Real &) const;
}
Real r;
Complex c;
Base & b = r;
b = b + c;
[A] Base::operator+ (const Base &) const
[B] Real::operator+ (const Base &) const
[C] Real::operator+ (const Complex &) const
[D] Complex::operator+ (const Real &) const
odpoved:
B
(jsou dve faze:
1) overloading
2) virtual methods
1) overloading je pri prekladu, zjistuje se ktera funkce ze vsech moznych se na zaklade parametru zavola. B je base, C je complex, B + C je operator+ becka s parametrem Complex. B je Base a Base ma jedinou moznost 'Base operator+ (const Base &) const'. kdyby v Base bylo treba 'Complex operator+ (const Complex &) const' tak by se volalo Complex a ne Base. proste se koukne a najde nejlevnejsi overloading. tady je jediny mozny.
2) za behu: za behu se pers * a & pouziva VMT, takze v b je Real r, takze se pouzije VMT a koukne se do Real::operator+ (const Base &) const)