Volá se operátor přiřazení rodičovské třídy, nikoliv potomka – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Volá se operátor přiřazení rodičovské třídy, nikoliv potomka – C / C++ – Fórum – Programujte.comVolá se operátor přiřazení rodičovské třídy, nikoliv potomka – C / C++ – Fórum – Programujte.com

 

yaqwsx+9
Posthunter
4. 2. 2010   #1
-
0
-

Mám problém s operátorem přiřazení u děděných tříd.
Mám základní třídu A a potomka B. nechávám si vygenerovat konstruktory a operátory přiřazení kompilátorem. Chci využívat polymorfismus, tak mám nadefinovaou obslužnou třídu:

#pragma once


template <class T> class Obsluha {
public:
Obsluha(): p(0) { }
Obsluha(const Obsluha& s){delete p; p = s.p->klon();}
Obsluha& operator=(const Obsluha&);
~Obsluha() { delete p; }

Obsluha(T* t): p(t) { }

operator bool() const { return p; }
T& operator*() const;
T* operator->() const;

private:
public:
T* p;
};

#include <stdexcept>

using std::runtime_error;

template <class T>
Obsluha<T>& Obsluha<T>::operator=(const Obsluha& pso)
{
if (&pso != this) {
delete p;
p = pso.p->klon();
}
return *this;
}

template <class T>
T& Obsluha<T>::operator*() const
{
if (p)
return *p;
return *p;
}

template <class T>
T* Obsluha<T>::operator->() const
{
if (p)
return p;
return 0;
}

Když ale potom mám kód:


vector<Obsluha<A> > data;
Obsluha<A> p1 = new B;
p1->nacist();//načtení dat do struktury
data.push_back(p);//zde nastává problém

V tomto kódu se mi do vectoru zkopírují jenom atributy základní třídy. Když jsem si nadefinoval operátory přiřazení a kopy kontruktory a dal do nich breakpoint, tak jsme zjistil, že aplikace nikdy operátor přiřazení a kopy konstruktor nikdy nevolá.

Nevíte kde může být chyba?

Nahlásit jako SPAM
IP: 85.160.112.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Jura
~ Anonymní uživatel
637 příspěvků
4. 2. 2010   #2
-
0
-

Zdravím,

z tvého popisu příliš moudrý nejsem, ale tipoval bych, že máš nejspíše špatně logiku kopírování.



#include <exception>
#include <iostream>
#include <vector>

template <class T> class Obsluha {
public:
Obsluha(): p(0) { }

Obsluha(const Obsluha& s) : p(0) {
p = s->klon();
}

Obsluha& operator=(const Obsluha&);

~Obsluha() throw() {
delete p;
}

Obsluha(T* t): p(t) {}

operator bool() const { return ( 0 != p ); }
T& operator*() const;
T* operator->() const;

private:
T* p;
};


template <class T>
Obsluha<T>& Obsluha<T>::operator=(const Obsluha& pso)
{
if (&pso != this) {
delete p;
p = pso->klon();
}
return *this;
}

template <class T>
T& Obsluha<T>::operator*() const
{
if (p)
return *p;

throw std::runtime_error("Invalid pointer");
}

template <class T>
T* Obsluha<T>::operator->() const
{
if (p)
return p;

throw std::runtime_error("Invalid pointer");
}

class A
{
public:

A(int val)
: val_(val)
{}

virtual void printData(std::ostream& os) const {
os << "Tohle je trida A " << val_ << std::endl;
}

virtual A * klon() const {
return new A(*this);
}

protected:
A(const A& other) {
val_ = other.val_;
}

private:
// zakazano
A& operator=(const A& );

private:
int val_;
};

class B : public A
{
public:

B(double real, int val)
: A(val), real_(real)
{
}

virtual void printData(std::ostream& os) const {
os << "Tohle je trida B " << real_ << std::endl;
A::printData(os);
}

virtual A * klon() const {
return new B(*this);
}

protected:

B(const B& other)
: A(other)
{
real_ = other.real_;
}

private:
// zakazano
B& operator=(const B& );

private:
// atributy
double real_;
};

int main()
{
try
{
std::vector<Obsluha<A> > data;
for( int i = 0; i < 10; ++i ) {
Obsluha<A> p = ( i % 2 ) ? new A(i) : new B(i+0.5, i);
data.push_back(p);
}

data[1] = data[0];

for( int j = 0; j < data.size(); ++j ) {
data[j]->printData(std::cout);
std::cout << std::endl;
}


}
catch(std::exception& e) {
std::cerr << e.what();
}
return 0;
}

Nahlásit jako SPAM
IP: 85.207.192.–
yaqwsx+9
Posthunter
6. 2. 2010   #3
-
0
-

To Jura : Díky za odpověď, ale moc jsem ji nepochopil. Zkusím ještě jednou a líp popsat můj problém.
Mám třídy A a B jak jsi popsal výše, jen jsem zapomněl uvést, že třída A je abstraktní. Když si vytvořím Obsluha<A> a = new B, je vše v pořádku. Ale jakmile zavolám data.push_back(a); tak do vectoru se mi přidá prvek, ale inicializovány jsou pouze hodnoty, které definuje A, hodnoty které definuje B zůstou neinicializovány.

Pokud se ještě dnes dostanu ke stolnímu PC přidám zde i konkrétní zdrojové kódy (píšu z NTB)

Nahlásit jako SPAM
IP: 85.160.123.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
yaqwsx+9
Posthunter
6. 2. 2010   #4
-
0
-

Přikládám hlavičkový soubor, zdrojový soubor v tomto případě nehraje roli:



class zbran
{
public:
enum{STRELNA=0, LUK=1, UDERNA= 2};
zbran(void);
virtual ~zbran(void);
virtual void pohyb(int x, int y) = 0;
virtual void vykreslit(int x, int y, int vrst) = 0;
virtual void nacist(ifstream& s);
virtual void ulozit(fstream& s);
virtual zbran* klon() = 0;
private:
public:
sprite ikona;
string nikona;
int popis;
int typ;
int druh;
};


class strelnazbran: public zbran
{
public:
strelnazbran(void);
~strelnazbran(void);
void pohyb(int x, int y);
void vykresleni(int x, int y, int vrst);
void pridatnaboje(int i);
void nacist(ifstream& s);
void ulozit(fstream& s);
zbran* klon(){zbran* p = new strelnazbran; (*p) = (*this); return p;};

private:
public:
koncetina zbr;
double naboje, kapacitazasobniku, vzasobniku;
naboj n;
vector<okoncetina> o;
vector<sprite> l, p;
string z;
};

Nahlásit jako SPAM
IP: 85.160.123.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Wizard0
Stálý člen
6. 2. 2010   #5
-
0
-

A ako presne si zistit, ze niesu initializovane? Pokial chces pristupovat k premenam, funkciam potomka pouzitim ukazatela na predka (ktory ukazuje samozrejme na objekt potomka) musis ho pomocou static_cast pretypovat na ukazatel na potomka. Btw toto nieje asi dobry napad:

Obsluha(const Obsluha& s){delete p; p = s.p->klon();}


ked volas konstrutktor tak to znamena, ze objekt sa vytvori => ukazatel 'p' je neplatny, ked ho zmazes sposoby to pad programu.

Nahlásit jako SPAM
IP: 85.216.193.–
yaqwsx+9
Posthunter
6. 2. 2010   #6
-
0
-

To Wizard : To, že nejsou inicializované jsem zjistil pomocí debuggeru (prvně díky tomu, že vectory l a p jsou prázdné a prgram mi házel vyjímku)
K tomu kódu, teď na to čumím co to je za blbost. Nevím kde vznikla chyba (jestli jsem si omylem smazal funkci, když jsem to tu kopíroval a nějak špatně jsme ji dopsal). V kódu mám:

Obsluha(const Obsluha& s){ p = s.p->klon();}

Nahlásit jako SPAM
IP: 85.160.123.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Wizard0
Stálý člen
6. 2. 2010   #7
-
0
-

Akosi nevidim operator priradenia v triede 'zbran' aj ked ho volas vo funkcii 'strelnazbran::klon'. (to by sa ti sice asi ani neskopilovalo)

EDIT: Osobne si niesom isty ako kompilator vytvori operatory priradenia (ani som nevedel, ze nejake vytvara). Mozno by ale pomohlo keby si funkciu klon upravil, tak aby si implicitne volal operator priradenia potomka:



(*p) = (*this) ------ *static_cast<strelnazbran*>(p) = *this;

Nahlásit jako SPAM
IP: 85.216.193.–
Jura
~ Anonymní uživatel
637 příspěvků
7. 2. 2010   #8
-
0
-

Zdravím,

tak teď už je to jasné. Jak jsem psal výše, máš špatně navržené kopírování. Jde o to, že v případě, kdy neuvedeš svůj vlastní kopy konstruktor a operátor přiřazení, jej za tebe vygeneruje sám kompilátor. Jenže takhle předdefinované metody nemusí nutně dělat to, co očekáváš. Konkrétně v tvém případě jde ještě o to, že takto vygenrovaný operátor přiřazení není virtualní, tudíž kód:



zbran* p = new strelnazbran;
(*p) = (*this);
return p;


zavolá operátor přiřazení třídy,který je jeho levým operandem - tedy operátor třídy zbran. A právě díky tomu, že není virtuální se volá jen operátor bazové třídy. Nyní tě už asi samtoného napadá několik řešení, např.:
1) ve třídě zbran definovat vlastní virtualní operátor přiřazení a v potomcích pak přepisovat tento operátor(bacha na signaturu operatoru)
2) definovat normalní polymorfní třídu - kopírování povolit jen přes metodu klon(clone) a kopírování řídit volaním (chráněného) kopy konstruktoru.
3) atd...

Nahlásit jako SPAM
IP: 85.207.192.–
yaqwsx+9
Posthunter
7. 2. 2010   #9
-
0
-

To Wizard : To Jura : Moc díky už to funguje. Tento podstatný detail mi pořád unikal...

EDIT: A už je mi i jasný, proč mi to s jinými třídami fungoval. Zase jsem udělal chybu z nepozornosti; správně by funkce klon měla vypadat takto:

zbran* klon(){strelnazbran* p = new strelnazbran; *p = *this; return p;}

místo zbran* má být strelnazbran*

Nahlásit jako SPAM
IP: 85.160.88.–
Life is too short to remove USB mass storage safely...
Správný drsňák udělá z konzole cokoliv
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 142 hostů

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý