tags:

views:

137

answers:

4

I'm trying to make a class inherits from other and override some methods. Classes 'header' is:

class Objeto {  
public:  
    virtual bool interseca(const Rayo &rayo, float magnitud);  
    virtual bool breakNormal(const Punto &punto);  
    virtual Vector normal(const Punto &punto);  

    int idMaterial;  
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca(const Rayo &rayo, float magnitud);
    // etc
};

Next in other place of the program (outside of Objeto and Esfera) I do:

// ObjectList is a Vector<Objeto>
Objeto o = esfera; /* Where esfera is a valid Esfera object */
ObjectList[0] = o;
ObjectList[0].interseca(rayo, magnitud);

What I want is to call the new version of interseca that is in Esfera. In this way I can add more objects (Cube, Triangle, etc) and treat them as generic "Objetos".

But instead of the Esfera implementation of interseca the program calls Objeto::interseca.

What is the correct way to do this override with C++? Is that the way to do overriding and I'm missing something or I'm plain wrong? Any tip or alternative to do that?

A: 

You created an Objeto on the stack. This item always has the type Objeto. For polymorphism you must use a reference or pointer.

DeadMG
The allocation of the object has nothing to do with it. Using a reference or a pointer does not require the object be allocated on the heap.
Merlyn Morgan-Graham
yea, but I think it's valuable to note the confusion. Objeto o = esfera is creation of an Objeto on the stack, instead of what the asker intended to do. That's what I think DeadMG means.
Gary
+6  A: 

You can only get polymorphic behavior if you access the class via a pointer or reference. In addition to that, you are slicing the object when you are copying the derived type to a non-pointer/non-reference base type.

Slicing:

http://en.wikipedia.org/wiki/Object_slicing

Given these definitions:

#include<iostream>

class Objeto {  
public:  
    virtual bool interseca() {
        return false;
    }
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca() {
        return true;
    }
};

This won't do what you want:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";

false

But this will:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";

true

Program design

A technique you can use to avoid this in the future is to make your base classes (pure) abstract.

Would you ever instantiate a true Objeto in your scene, or are you simply defining a base type? If you are just defining a base type (which I recommend), then you can make interseca, breakNormal, and normal pure virtual. Then, the compiler will catch problems like the one you have here.

class Objeto {  
public:  
    virtual bool interseca() = 0;
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca()
    {
        return true;
    }
};

// ...

Then, this will be okay:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";

compilation succeded

But this will cause the compiler to error - a good thing, cause it's catching a bug:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";

error: cannot allocate an object of abstract type 'Objeto'

Merlyn Morgan-Graham
+3  A: 

Your overriding is correct, but it seems you haven't fully grasped C++' object model yet. This

Objeto o = esfera;

doesn't do what you think it does. This creates a copy of the Objeto sub-object of esfera into o. (This is called "slicing".) What you want instead is to reference esfera using either a reference

Objeto& o = esfera; // note the & 

or o pointer

Objeto* o = &esfera;

If in doubt, always prefer references.

sbi
+2  A: 

You have overridden the method successfully. The problem is in the way you store objects and in the way polymorphic behaviour is achieved in C++.

To get polymorphic behaviour, you have to reference an object with a pointer or with a reference. C++ doesn't support reference type semantics in the same way as Java or C#. If you have a variable created like this

Objeto o = esfera;

variable o is a new object of the (static and dynamic) type Objeto created based on the object esfera. What happens here is called object slicing.

To get polymorphic behaviour you have to use a pointer or a reference. For example let's assume variables objeto and esfera are of the types Objeto and Esfera respectively.

Objeto* op = &objeto;
op->interseca(rayo, magnitud); // virtual dispatch is used but since op points
                               // to an object of type Objeto Objeto::interseca
                               // gets called.
op = &esfera;
op->interseca(rayo, magnitud); // virtual dispatch is used and this time
                               // Esfera::interseca gets called.

You can't store objects of different types in one vector. If you create a vector<Objeto> and try to store an object of type Esfera in it object slicing will happen just like in the example with variable o.

To have a container, that allows you to get polymorphic behaviour, you have to create a vector of some kind of pointers. That means you will have to solve a problem of the ownership of the objects, vector elements will reference. The best solution would most probably be to use a vector of some smart pointers.

Maciej Hehl
C++ *does* support reference type semantics like Java and C# - moreover, if you use reference semantics in the same way, you do get the expected polymorphic behaviour.
Arafangion
@Arafangion Oh really? So enlighten me please, why OP got object slicing with the code that would work in C#?
Maciej Hehl
The OP specifically said in his code that he wanted an object of type Objeto. He initialized that object with an Esfera object, so some kind of conversion took place. If he said instead that he merely wanted to *refer* to the Esfera object, then everything would've worked as he expected.
Arafangion
@Arafangion Yeah if he said he wanted to refer. The point is, that he had to say it explicitly. Here is the difference in the way you and I understand language support for reference semantics. Fine C++ does support it, but you have to write wrappers for pointers yourself and overload operator= for them. `int* ip = ` is not a reference semantics. It's a value semantics for a pointer. Anyway, I changed my answer to say, that the support is not in the same way. I hope you agree with that.
Maciej Hehl