Questions about polymorphism, dynamic binding, and virtual functions

How do I overwrite functions when parameter types can change?

Page 1 of 1

9 Replies - 3183 Views - Last Post: 25 November 2010 - 10:06 PM Rate Topic: -----

#1 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Questions about polymorphism, dynamic binding, and virtual functions

Posted 24 November 2010 - 02:13 PM

Hello. I pretty much have the gist of what I am trying to do in my topic and topic description. For the assignment that I am working on, I have to write a program that manages various types of numbers. I have a superclass, multinumber, with various functions:

	virtual Multinumber operator+(Multinumber);
	virtual Multinumber& operator=(Multinumber);
	virtual bool operator==(Multinumber);
	virtual string object2string();
	virtual void makeobject(double, double)=0;


Then I have three subclasses (Pairs, Complex, and Rational), and in each of these subclasses I have to override each of these functions. This is turning out to be quite problematic though because the parameters for the virtual functions in Multinumber are of type Multinumber, and Multinumber is, obviously, an abstract class. I was hoping that since the other three classes are inherited from Multinumber the compiler wouldn't mind if I, for example, defined a function in Complex as

Complex operator+(Complex);



But the compiler is not recognizing this as the same function definition.
The class, Multinumber, looks like this:

#include "Multinumber.h"
using namespace std;

Multinumber::operator+(Multinumber num)
{
}

Multinumber::operator=(Multinumber num)
{
}

Multinumber::operator==(Multinumber num)
{
}

Multinumber::object2string()
{
}

Multinumber::makeobject(double x, double y)
{
}




In essence, I am trying to make all of the above functions dynamically binded but using different parameters so that I can use them polymorphically in a set data structure (for example, I cannot add a "Complex" to a "Rational.") Am I doing this incorrectly? I think I understand the theory behind it, but I am a little confused. Thank you for any help you can give me. If needed, I have included the relevant code as an attachment.

Attached File(s)

  • Attached File  code.zip (4.26K)
    Number of downloads: 63

This post has been edited by lunixer: 24 November 2010 - 02:16 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Questions about polymorphism, dynamic binding, and virtual functions

#2 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 24 November 2010 - 05:28 PM

View Postlunixer, on 24 November 2010 - 04:13 PM, said:

I was hoping that since the other three classes are inherited from Multinumber the compiler wouldn't mind if I, for example, defined a function in Complex as

Complex operator+(Complex);



Why do you want to do that? Isn't that exactly what Oler1s said was wrong with my original suggestion a few days ago? All of your operator overloads (in fact, all of your functions that take any kind of MultiNumber argument) should take MultiNumber (base class) references.

Here's an example of an abstract base class and two derived classes. The virtual function void greet( base *) is defined in all 3 classes, and the actual type of each argument is bound to each pointer at runtime so the version that is called is the one that belongs to the proper derived class:

#include <iostream>
#include <typeinfo>
using namespace std;

class base {
  private:
    string name;
  
  public:
    base() {}
    void setname( const char* n ) { name = n;}
    string getname() const { return name; }
    virtual void greet(const base &) = 0;
};

class derived1 : public base {
  private:
    
  public:
    derived1() {}
    derived1( const char* n) { setname(n); }

    void greet( const base& b ) {
        cout << "hello " << b.getname() << ", I'm " << getname() << endl;
    }
};

class derived2 : public base {
    private:

    public:
        derived2() {}
        derived2( const char* n ) { setname(n); }
        
        void greet( const base& b ) {
            cout << "howdy " << b.getname() << ", I'm " << getname() << endl;
        }
};

int main () {
  base *BP1, *BP2, *BP3;
  BP1 = new derived1("nancy");
  BP2 = new derived1("bob");
  BP3 = new derived2("bill");
  
  cout << boolalpha << ( typeid(*BP1)==typeid(derived1) ) << endl;
  cout << ( typeid(*BP1)==typeid(derived2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP3) ) << endl;
  
  BP2->greet(*BP3);
  BP3->greet(*BP2);
}



Does that answer your question?


Edit: replaced pointers with const references in the greet methods. If, for example, you want to add two Complex objects, the operands should be references, not pointers (although not necessarily const, depending on the operation).

This post has been edited by r.stiltskin: 24 November 2010 - 08:18 PM

Was This Post Helpful? 2
  • +
  • -

#3 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 24 November 2010 - 08:47 PM

View Postr.stiltskin, on 24 November 2010 - 06:28 PM, said:

View Postlunixer, on 24 November 2010 - 04:13 PM, said:

I was hoping that since the other three classes are inherited from Multinumber the compiler wouldn't mind if I, for example, defined a function in Complex as

Complex operator+(Complex);



Why do you want to do that? Isn't that exactly what Oler1s said was wrong with my original suggestion a few days ago? All of your operator overloads (in fact, all of your functions that take any kind of MultiNumber argument) should take MultiNumber (base class) references.

Here's an example of an abstract base class and two derived classes. The virtual function void greet( base *) is defined in all 3 classes, and the actual type of each argument is bound to each pointer at runtime so the version that is called is the one that belongs to the proper derived class:

{...}

Does that answer your question?


Edit: replaced pointers with const references in the greet methods. If, for example, you want to add two Complex objects, the operands should be references, not pointers (although not necessarily const, depending on the operation).


Extremely helpful. Thank you. I guess I didn't really understand what you meant in the other thread, but after rereading it now I do. But I am running into problems doing it that way, with everything passed by reference as a Multinumber. The biggest problem that I have encountered is this: Multinumber has no functions and no attributes. Therefore, whenever I try to write something like this:

bool Complex::operator==(const Multinumber &rhs) const
{
    return ((real == rhs.real) && (imag == rhs.imag));
}



I get a compiler error:
/home/ross/flash/current/CS260/assignment6/Complex.cpp|31|error: 'const class Multinumber' has no member named 'real'|



And the same with the variable imag, as well as in all of the other functions that treat a Multinumber as a pair, complex, or rational. So, how do I fix this? Do I have to cast the multinumber in all of these locations? I can't really figure out another solution.

Also, while I have you on the line, I have another mysterious compiler error concerning this code:
const Complex& Complex::operator+(const Complex & rhs) const
{
    return &Complex(real + rhs.real, imag + rhs.imag);
}



/home/ross/flash/current/CS260/assignment6/Complex.cpp|26|warning: taking address of temporary|
/home/ross/flash/current/CS260/assignment6/Complex.cpp|26|error: invalid initialization of reference of type 'const Complex&' from expression of type 'Complex*'|



Any idea what that means? Thank you *so* much for your help.
Was This Post Helpful? 0
  • +
  • -

#4 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 24 November 2010 - 09:46 PM

Look more carefully at my example. There must be at least 1 virtual function which is defined in the base class as well as in the derived classes in order for the classes to be polymorphic. And you want the base class to be abstract (to prevent instantiation of base class objects) so there must be at least 1 PURE virtual function.

I believe that all of your operator overloads need to be virtual methods: the same signature defined in the base class and in the derived classes. The one in the base class doesn't have to DO anything at all -- the actual functionality will be provided in each of the derived class definitions.

I think your error "has no member named 'real'" happened because your class hierarchy is not polymorphic (see 1st paragraph).

As to the other error -- you have created a Complex object that exists only within the function's scope but you are returning that address to the calling function. The address will be useless after the operator+ function exits. (Use new to allocate the object.)
Was This Post Helpful? 2
  • +
  • -

#5 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 09:00 AM

View Postr.stiltskin, on 24 November 2010 - 10:46 PM, said:

Look more carefully at my example. There must be at least 1 virtual function which is defined in the base class as well as in the derived classes in order for the classes to be polymorphic. And you want the base class to be abstract (to prevent instantiation of base class objects) so there must be at least 1 PURE virtual function.

I believe that all of your operator overloads need to be virtual methods: the same signature defined in the base class and in the derived classes. The one in the base class doesn't have to DO anything at all -- the actual functionality will be provided in each of the derived class definitions.

I think your error "has no member named 'real'" happened because your class hierarchy is not polymorphic (see 1st paragraph).

As to the other error -- you have created a Complex object that exists only within the function's scope but you are returning that address to the calling function. The address will be useless after the operator+ function exits. (Use new to allocate the object.)


Assuming that I understood you correctly, my class hierarchy is definitely polymorphic.


class Multinumber
{
public:
    //Preconditions: two multinumbers to add
    //Postconditions: added together based on definition in subclass
	virtual Multinumber& operator+(Multinumber&);
    //Preconditions: one multinumber to set equal to another, same memory allocation
    //Postconditions: multinumber set equal to ther other
	virtual Multinumber& operator=(Multinumber&);
    //Preconditions: two multinumbers to compare
    //Postconditions: bool returned that says whether multinumbers are equivalent as defined by programmer
	virtual bool operator==(Multinumber&);
    //Preconditions: none
    //Postconditions: string written out that defines important information about object, as defined by programmer, replaces <<
	virtual string object2string();
    //Preconditions: already created multinumber object
    //Postconditions: calls appropriate constructor again to redefine object with new numbers, replaces >>
	virtual Multinumber& makeobject(double, double)=0;

};



I have one pure virtual function. All of my operator overloads are virtual. Here is the header from the Complex class (the one I am focusing on now):

class Complex : public Multinumber
{
 private:
    double real;
    double imag;

 public:
    //Preconditions: none
    //Postconditions: constructor, first variable becomes 'real,' second variable 'imag'
    Complex (double x=0.0, double y = 0.0);
    //Preconditions: two multinumbers to add
    //Postconditions: added together, fails if not of complex type
    const Complex& operator+ (const Multinumber &) const;
	//Preconditions: two multinumbers to compare
    //Postconditions: bool returned that says whether multinumbers are equivalent, fails if not complex type
	bool operator==(const Multinumber&) const;
    //Preconditions: one complex to set equal to another, same memory allocation
    //Postconditions: complex set equal to ther other, fails if multinumber is not complex
	Multinumber& operator=(const Multinumber&);
 	//Preconditions: none
    //Postconditions: string written out that defines important information about object, as defined by programmer, replaces <<
	string object2string();
  	//Preconditions: already created complex object
    //Postconditions: calls appropriate constructor again to redefine object with new numbers, replaces >>
	Multinumber& makeobject(double, double);

};



Likewise, the pure virtual function and other functions are all overridden. The class is declared to be an extension of multinumber. All of my parameters are multinumbers. But I still get:

/home/ross/flash/current/CS260/assignment6/Complex.cpp|33|error: 'const class Multinumber' has no member named 'real'|



Do you have an idea why this might be? I'm going to attach my code so far to this post.

Also, with respect to the other problem I was having, I took your advice:

const Multinumber& Complex::operator+(const Multinumber & rhs) const
{
    return new Complex(real + rhs.real, imag + rhs.imag);
}



but I am still not quite sure why this works. Isn't the new Complex that I am returning a Complex object, rather than a Complex&? And where would I put the corresponding 'delete' token to this new? Do I have to put the delete wherever I use the + operator? I'm a little concerned about memory leaks. Once more, thank you so much for your help.

Attached File(s)

  • Attached File  code.zip (5.19K)
    Number of downloads: 52

Was This Post Helpful? 0
  • +
  • -

#6 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 10:39 AM

Right, a base class pointer or reference can't access a member that doesn't exist in the base class. Here's a modified example that should solve that problem.

#include <iostream>
#include <typeinfo>
using namespace std;

class base {
  private:
//    string name;
  
  public:
    base() {}
//    void setname( const char* n ) { name = n;}
//    string getname() const { return name; }
    virtual void greet(const base &) = 0;
};

class derived1 : public base {
  private:
    string name;
  public:
    derived1() {}
//    derived1( const char* n) { setname(n); }
    derived1( const char* n ) { name = n; }

    string getname() const { return name; }
    void greet( const base& b ) {
        if( typeid(B)/> == typeid(derived1) ) {
            derived1* temp = (derived1*)&b;
            cout << "hello " << temp->getname() << ", I'm " << getname() << endl;
        }
        else cout << "Sorry, I can't speak to you.\n";
    }
};

class derived2 : public base {
    private:
        string name;

    public:
        derived2() {}
//        derived2( const char* n ) { setname(n); }
        derived2( const char* n ) { name = n; }
        
        string getname() const { return name; }
        void greet( const base& b ) {
            if( typeid(B)/>==typeid(derived2) ) {
                derived2* temp = (derived2*)&b;
                cout << "howdy " << temp->getname() << ", I'm " << getname() << endl;
            }
            else cout << "Sorry, I can't speak to you.\n";
        }
};

int main () {
  base *BP1, *BP2, *BP3;
  BP1 = new derived1("nancy");
  BP2 = new derived1("bob");
  BP3 = new derived2("bill");
  
  cout << boolalpha << ( typeid(*BP1)==typeid(derived1) ) << endl;
  cout << ( typeid(*BP1)==typeid(derived2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP3) ) << endl;
  
  BP1->greet(*BP2);
  BP2->greet(*BP1);
  BP1->greet(*BP3);
}



As to the other issue, I think I was wrong to suggest using new in the overload function. Instead, I think it should return an object (not a reference) by value:

const Multinumber Complex::operator+(const Multinumber & rhs) const
Was This Post Helpful? 0
  • +
  • -

#7 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 12:49 PM

View Postr.stiltskin, on 25 November 2010 - 11:39 AM, said:

Right, a base class pointer or reference can't access a member that doesn't exist in the base class. Here's a modified example that should solve that problem.

#include <iostream>
#include <typeinfo>
using namespace std;

class base {
  private:
//    string name;
  
  public:
    base() {}
//    void setname( const char* n ) { name = n;}
//    string getname() const { return name; }
    virtual void greet(const base &) = 0;
};

class derived1 : public base {
  private:
    string name;
  public:
    derived1() {}
//    derived1( const char* n) { setname(n); }
    derived1( const char* n ) { name = n; }

    string getname() const { return name; }
    void greet( const base& b ) {
        if( typeid(B)/> == typeid(derived1) ) {
            derived1* temp = (derived1*)&b;
            cout << "hello " << temp->getname() << ", I'm " << getname() << endl;
        }
        else cout << "Sorry, I can't speak to you.\n";
    }
};

class derived2 : public base {
    private:
        string name;

    public:
        derived2() {}
//        derived2( const char* n ) { setname(n); }
        derived2( const char* n ) { name = n; }
        
        string getname() const { return name; }
        void greet( const base& b ) {
            if( typeid(B)/>==typeid(derived2) ) {
                derived2* temp = (derived2*)&b;
                cout << "howdy " << temp->getname() << ", I'm " << getname() << endl;
            }
            else cout << "Sorry, I can't speak to you.\n";
        }
};

int main () {
  base *BP1, *BP2, *BP3;
  BP1 = new derived1("nancy");
  BP2 = new derived1("bob");
  BP3 = new derived2("bill");
  
  cout << boolalpha << ( typeid(*BP1)==typeid(derived1) ) << endl;
  cout << ( typeid(*BP1)==typeid(derived2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP3) ) << endl;
  
  BP1->greet(*BP2);
  BP2->greet(*BP1);
  BP1->greet(*BP3);
}



As to the other issue, I think I was wrong to suggest using new in the overload function. Instead, I think it should return an object (not a reference) by value:

const Multinumber Complex::operator+(const Multinumber & rhs) const


I'm terribly sorry, but I'm not sure what you're trying to show me with that code sample. In your main() all of the functions that are accessed are member functions of the base class. Also, I can't return an object by value in the operator+() function because Multinumber is abstract and I can't instantiate it. Once more, thank you for your help.

EDIT: I see, you think I should use typeid to figure out what type something is before I act on it. My professor said specifically that I'm not allowed to do that. There is some way to do this without testing for types beforehand, but I'm at a loss for what it is.

This post has been edited by lunixer: 25 November 2010 - 12:57 PM

Was This Post Helpful? 0
  • +
  • -

#8 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 07:34 PM

OK, here's a solution that doesn't involve typeid. I used dynamic_cast to cast the base pointer to a derived class pointer. The cast fails (and returns 0) if the object pointed to is an instance of the wrong derived class.

#include <iostream>
#include <string>
using namespace std;

class base {
  private:

  public:
    base() {}
    virtual void greet(const base &) = 0;
};

class derived1 : public base {
  private:
    string name;
    int value;

  public:
    derived1() {}
    derived1( const char* n ) : name(n) {}
    derived1( const char* n, int v ) : name(n), value(v) {}

    int getvalue() const { return value; }
    void setvalue( int v ) { value = v; }
    string getname() const { return name; }
    void setname( string n ) { name = n; }
    void greet( const base& b ) {
        if( const derived1* temp = dynamic_cast<const derived1*>(&B)/> ) {
            cout << "hello " << temp->getname() << ", I'm " << getname() << endl;
        }
        else cout << "Sorry, I can't speak to you.\n";
    }


};

class derived2 : public base {
    private:
        string name;

    public:
        derived2() {}
        derived2( const char* n ) : name(n) {}

        string getname() const { return name; }
        void greet( const base& b ) {
            if( const derived2* temp = dynamic_cast<const derived2*>(&B)/> ) {
                cout << "howdy " << temp->getname() << ", I'm " << getname() << endl;
            }
            else cout << "Sorry, I can't speak to you.\n";
        }
};

        

int main () {
  base *BP1, *BP2, *BP3, *BP4;
  BP1 = new derived1("nancy");
  BP2 = new derived1("bob");
  BP3 = new derived2("bill");
  BP4 = new derived2("alice");


  BP1->greet(*BP2);
  BP2->greet(*BP1);
  BP1->greet(*BP3);
  BP4->greet(*BP3);
}



I don't know the solution to your operator overload problem (although I wonder if dynamic_cast can be used there as well). I'd like to see how you solve this, so please post again when you make some progress.

This post has been edited by r.stiltskin: 25 November 2010 - 07:34 PM

Was This Post Helpful? 1
  • +
  • -

#9 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 1
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 07:58 PM

View Postr.stiltskin, on 25 November 2010 - 08:34 PM, said:

OK, here's a solution that doesn't involve typeid. I used dynamic_cast to cast the base pointer to a derived class pointer. The cast fails (and returns 0) if the object pointed to is an instance of the wrong derived class.

I don't know the solution to your operator overload problem (although I wonder if dynamic_cast can be used there as well). I'd like to see how you solve this, so please post again when you make some progress.


Yeah, somebody on stackoverflow recommended dynamic cast. That seems to have solved both of my problems. Thanks for your help!
Was This Post Helpful? 0
  • +
  • -

#10 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Questions about polymorphism, dynamic binding, and virtual functions

Posted 25 November 2010 - 10:06 PM

You can't dynamic_cast an object; it can only be used on a pointer or a reference, so presumably you have to return a reference. That's fine for an assignment operator that returns a reference to the already-existing lhs object, but normally a + operator should return a new object, not a reference.

Also, you can't return a base class object (see your code line 1 in post #1) because the base class is abstract.

So how do you avoid the memory leak that occurs if you return a reference to an object that was dynamically allocated in the overload function? (Or is there some other solution?)

This post has been edited by r.stiltskin: 25 November 2010 - 10:19 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1