mboost-dp1

Problemer med polymorfisme i C++


Gå til bund
Gravatar #1 - Spiderboy
8. okt. 2008 18:50
Hejsa. Som nævnt i overskriften har jeg lidt bøvl med polymorfisme i C++. Følgende program demonstrerer mit problem:

#include <iostream>
#include <vector>

class Base {
public:
virtual void print() {std::cout << "Base\n";}
};

class Derived : public Base {
public:
void print() {std::cout << "Derived\n";}
};

int main(int argc, char* argv[]) {
std::vector<Base> v;

v.push_back(Derived());
v.at(0).print();
}


Programmet outputter "Base", men jeg vil gerne have den til at outputte "Derived".

Jeg har en mistanke om, at problemet opstår, fordi det på en eller anden måde er Base's copy constructor, som bliver kaldt i linje 17, men jeg ved det ikke.

Problemet kunne formentlig løses ved at bruge pointere direkte, dvs. noget bruge noget a la std::vector<Base*>, men det vil jeg helst undgå, hvis det er muligt.

Hvad skal jeg gøre?
Gravatar #2 - m_abs
8. okt. 2008 19:38
Du skal bruge pointere for at opnå det du prøver på.

Det der sker er at dit Derived-object bliver kopieret til et Base objekt i linie 17 og det vil aldrig ske fordi du kommer en kopi ind i din vector.

Hvis du derimod laver en vector med Base*, så kan du sætte en pointer til et Derived-objekt ind i din vector.

Det er i øvrigt pænest at sætte virtual på den nye funktion.

#include <iostream>
#include <vector>

class Base {
public:
virtual void print() {std::cout << "Base\n";}
};

class Derived : public Base {
public:
virtual void print() {std::cout << "Derived\n";}
};

int main(int argc, char* argv[]) {
std::vector<Base *> v;

v.push_back(new Derived());
v.at(0)->print();
}
Gravatar #3 - Spiderboy
8. okt. 2008 19:46
#2
Okay, det var også lige præcis hvad jeg regnede med - både hvad årsagen var, og hvad løsningen er. Tak for det.
Gravatar #4 - m_abs
8. okt. 2008 20:28
#3
Jeps :) Kan godt se jeg kunne have svaret bedre.

Jeg gætter på grunden til du helst ville undgå pointere, var for at slippe for skulle sørge for at slette dem efter brug.

Jeg ved ikke om du kender til smart pointers, men de vil løse det problem.

En smart pointer er en wrapper om en pointer til dit objekt som holder styr på hvor mange gange dit objekt er referet. Når sidste reference til objekted forsvinder bliver objektet slettet fra RAM.

Jeg plejer at bruge boosts shared_ptr da disse er meget nemme at bruge.
Det eneste det kræver er at man har boost installeret ved compile time, så man har deres headers tilrådighed (brugeren skal ikke have boost installeret for at køre programmet).
De fleste linux distriutioner har boost i deres pakkemanager, windows ved jeg ikke hvordan man installere men det skulle være nemt nok.

For at gøre det nemmere at forstå hvad jeg snakker om har jeg skrevet dit eksempel om, så det gør brug af arv og shared_ptr.
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

class Base {
public:
virtual void print() {std::cout << "Base\n";}
};

class Derived : public Base {
public:
virtual void print() {std::cout << "Derived\n";}
};

int main(int argc, char* argv[]) {
std::vector< boost::shared_ptr< Base > > v;

// Add derived object to our vector
Derived * d_ptr = new Derived( ); // Create a pointer to a Derived-object. this can be merged with the next line.
boost::shared_ptr<Base> d( d_ptr ); // Wrap the pointer in shared_ptr
v.push_back(d); // Add the wrapped pointer to the vector

// Should print "Derived"
v.at(0)->print();

// Print how many times d_ptr is used, should to 2
std::cout << d.use_count( ) << std::endl;

// Empty the vector to remove one reference to d
v.clear( );

// Print how many times d_ptr is used, should to 1
std::cout << d.use_count( ) << std::endl;

// Once this function goes out of scope d will be deleted and since use_count hits 0 d_ptr will also deleted.
}


Boosts hjemmeside http://www.boost.org/
Gravatar #5 - Spiderboy
8. okt. 2008 20:45
#4
Lige præcis. Dels for at pointere kan være trælse at holde styr på, og dels for at det gør koden mindre læsbar.

Ja, jeg kender lidt til smart pointers (i form af auto_ptr - ikke den fra Boost), men har ikke prøvet at bruge dem i praksis endnu.

Som du nok kan fornemme, så er jeg ikke superfortrolig med C++ endnu - jeg trækker rigtig meget på mine erfaringer fra andre programmeringssprog (f.eks. kendskab til de mange begreber, osv), så det går forholdsvist hurtigt og smertefrit med at sætte sig ind i C++. :-)
Gravatar #6 - arne_v
10. okt. 2008 02:17
#1

Følgende kode viser mere hvad der sker:

#include <iostream>
#include <vector>

using namespace std;

class P
{
protected:
int vp;
public:
P(int vp) { this->vp = vp; }
virtual void print() { cout << "P val: " << vp << " (@" << (int)this << ")" << endl; }
P(const P& o) { this->vp = o.vp; cout << "P copy: " << (int)&o << "->" << (int)this << endl; }
};

class C : public P
{
protected:
int vc;
public:
C(int vp, int vc) : P(vp) { this->vc = vc; }
virtual void print() { cout << "C val: " << vp << " " << vc << " (@" << (int)this << ")" << endl; }
C(const C& o) : P(o) { this->vc = o.vc; cout << "C copy: " << (int)&o << "->" << (int)this << endl; }
};

int main()
{
cout << "create P" << endl;
P op(123);
cout << "print P" << endl;
op.print();
cout << "create C" << endl;
C oc(456, 789);
cout << "print C" << endl;
oc.print();
vector<P> v;
cout << "save P in vector" << endl;
v.push_back(op);
cout << "save C in vector" << endl;
v.push_back(oc);
cout << "print vector" << endl;
v[0].print();
cout << "print vector" << endl;
v[1].print();
return 0;
}

Gå til top

Opret dig som bruger i dag

Det er gratis, og du binder dig ikke til noget.

Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.

Opret Bruger Login