Thread: Polymorphism, inheritance and containers

  1. #1
    Registered User
    Join Date
    Nov 2007
    Posts
    5

    Polymorphism, inheritance and containers

    Hi.
    I'm having a problem with inheritance and a container.
    Basically I have Graphic2D --> Polygon --> Rectangle (--> = parent of). The Graphic2D class is abstract and defines the interface the child classes, but without implementing any of them. The polygon defines the render() method that is to be used by itself and Rectangle. This is declared as a pure virtual function in Graphic2D.

    Inside another class I have a list container containing pointers to Graphic2D objects. When I iterate over this container and call the render() method on each object I get a runtime error relating to a pure virtual function call. I have done various tests and know 100% that it is calling the Graphic2D render() function every time.

    My understanding is that calling render() on these objects should call those in the child classes. Isn't that one of the whole points of polymorphism? I have recently started using namespaces too and I'm not sure if this has messed my inheritance structure in a way I don't understand.

    Help very much appreciated. My code is below.

    Code:
    //From renderer class - much more available if needed.
    void Renderer::RenderAll() {
    	screen.clear();
    
    	for (it = items.begin() ; it != items.end() ; it++) {
    		cout << "item for" << endl;
    		(*it)->render();                                                          /// calling line
    	}
    	/*for (int i = 200 ; i < 400 ; i++) {
    		setPixel(i,i);
    	}*/
    
    	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    
    	init_graphics();    // call the function to initialize the objects
    
        d3ddev->BeginScene();
    
    	d3ddev->SetFVF(CUSTOMFVF);	// select which vertex format we are using
    	d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));	// select the vertex buffer to display
    	d3ddev->DrawPrimitive(D3DPT_POINTLIST, 0, screen.size());	// copy the vertex buffer to the back buffer
    
        d3ddev->EndScene();
        d3ddev->Present(NULL, NULL, NULL, NULL);
    
        return;
    }
    
    ////////////////////////////Entire Graphic2D.hpp file - no corresponding cpp file
    
    #ifndef GRAPHIC2D_HPP
    #define GRAPHIC2D_HPP
    #include "Colour.hpp"
    #include "Vec2D.hpp"
    
    namespace MGL { //My Graphics Library
    	class Graphic2D {
    		public:
    			virtual ~Graphic2D() {};
    
    			Colour colour;
    			Colour fill_colour;
    			short thickness;
    			Vec2D<float> location;
    			Vec2D<short> orientation;
    
    			virtual void render() = 0;
    			virtual void fill() = 0;
    
    	};
    }
    #endif
    
    
    
    //////// Polygon.hpp and Polygon.cpp
    #ifndef POLYGON_HPP
    #define POLYGON_HPP
    #include <vector>
    #include "Graphic2D.hpp"
    #include "Line2D.hpp"
    #include "Vec2D.hpp"
    
    namespace MGL { //My Graphics Library
    
    	class Polygon : public Graphic2D {
    		public:
    			vector< Vec2D<float> > vertices;
    
    			Polygon();
    			Polygon(vector< Vec2D<float> > & vertices);
    			virtual ~Polygon();
    
    			void addVertex(Vec2D<float> & vertex);
    
    			virtual void render();
    			virtual void fill();
    	};
    }
    #endif
    
    #include "Polygon.hpp"
    using namespace MGL;
    using namespace std;
    
    namespace MGL {
    	Polygon::Polygon() {
    		vertices = vector< Vec2D<float> >(0);
    		orientation = Vec2D<short>(0,0);
    	}
    
    	Polygon::Polygon(vector< Vec2D<float> > & vertices) {
    		location = vertices[0];
    		this->vertices = vertices;
    		orientation = Vec2D<short>(0,0);
    	}
    
    	Polygon::~Polygon() {
    	}
    
    	void Polygon::render() {
    		cout << "polygon render" << endl;
    		int size = vertices.size();
    		for (int i = 0 ; i < size ; i++) {
    			Line2D(vertices[i], vertices[(i+1) % size]).render();
    		}
    	}
    
    	void Polygon::fill() {
    
    	}
    
    	void Polygon::addVertex(Vec2D<float> & v) {
    		if (vertices.size() == 0) {
    			location = v;
    		}
    		vertices.push_back(v);
    	}
    }
    
    
    /////////////Rectangle.hpp and Rectangle.cpp
    #ifndef RECTANGLE_HPP
    #define RECTANGLE_HPP
    #include "Vec2D.hpp"
    #include "Polygon.hpp"
    #include "Line2D.hpp"
    #include "Graphic2D.hpp"
    
    namespace MGL { //My Graphics Library
    
    	class Rectangle : public Polygon {
    
    		public:
    			Rectangle();
    			Rectangle(Vec2D<float> & location, float width, float height);
    			Rectangle(Vec2D<float> & location, float width);
    			~Rectangle();
    			void fill();
    	};
    
    }
    #endif
    #include "Rectangle.hpp"
    using namespace std;
    using namespace MGL;
    
    namespace MGL {
    	Rectangle::Rectangle() {
    	}
    
    	Rectangle::Rectangle(Vec2D<float> & location, float width, float height = -1) {
    		if (height == -1) {
    			height = width;
    		}
    
    		this->location = location;
    		float x = location.getX();
    		float y = location.getY();
    		vertices.resize(4);
    		vertices[0] = location;
    		vertices[1] = Vec2D<float>(x+width, y);
    		vertices[2] = Vec2D<float>(x+width, y+height);
    		vertices[3] = Vec2D<float>(x, y+height);
    	}
    
    
    	Rectangle::Rectangle(Vec2D<float> & location, float width) {
    		this->location = location;
    		float x = location.getX();
    		float y = location.getY();
    		vertices = vector< Vec2D<float> >(4);
    		vertices.resize(4);
    		vertices[0] = location;
    		vertices[1] = Vec2D<float>(x+width, y);
    		vertices[2] = Vec2D<float>(x+width, y+width);
    		vertices[3] = Vec2D<float>(x, y+width);
    	}
    
    	Rectangle::~Rectangle() {
    	}
    
    	void Rectangle::fill() {
    		render();
    		//Renderer.setColour(fill);
    		int x1 = (int) vertices[0].getX();
    		int y1 = (int) vertices[0].getY();
    		int width = (int) vertices[1].getX() - x1;
    		int height = (int) vertices[2].getY() - y1;
    		for (int y = y1; y < y1 + height; y++) {
    			Line2D(Vec2D<float>(x1+1,y), Vec2D<float>(x1+width,y)).render();
    		}
    	}
    }
    Sorry for the mammoth amounts, but unsure where the problem lies.
    Simon

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    This doesn't sound like something that should be hard to catch in the debugger - perhaps you can find it very quickly that way.

    I also can't see the code for Line2D::render() - is that declared anywhere?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Nov 2007
    Posts
    5
    Line2D.render() is declared elsewhere, I won't post the code here because it is very long. The active line is:
    Code:
    Renderer::instance().setPixel(x,y);
    I know this line works from a direct test ignoring my shapes altogether. My program will draw points to screen as needed, but currently it won't draw the shapes as I can't get access to the correct render() method on the Rectangle (or any other).

    Simon

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    So, does this happen on the first opject, or a subsequent object? What if you set a breakpoint on the line calling render, what is the object? Is it the object you expect it to be?

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    Nov 2007
    Posts
    5
    I currently don't have a debugger. I'm a uni student and we haven't properly been taught the use of them. The object is added in a wrapper Game.cpp class, on the following line:

    Code:
    	Vec2D<float> v(400,400);
    	Renderer::instance().add(&MGL::Rectangle(v, 100, 200));
    When I change this to add without the & operator it is rejected with the error:

    Code:
    no matching function for call to `Renderer::add(MGL::Rectangle)'
    note: candidates are: void Renderer::add(MGL::Graphic2D&)
    note: candidates are: void Renderer::add(MGL::Graphic2D*)
    This makes no sense to me. The definition of the two add functions are:

    Code:
    		void add(MGL::Graphic2D & g);
    		void add(MGL::Graphic2D * g);
    Surely the top one forces the pass by reference without the need to convert to a pointer with the & as I am doing atm. Is this part of the problem? If I change the function to copy instead of pass by reference, I am told that I can't do this since I have abstract functions in Graphic2D.

    Code:
    error: cannot declare parameter `g' to be of type `MGL::Graphic2D'
    error:   because the following virtual functions are abstract:
    error:  virtual void MGL::Graphic2D::fill()
    error:  virtual void MGL::Graphic2D::render()
    Changing these to empty functions gives me a compiler error relating to another use of add later on being ambiguous (due to the new copy function i assume), changing this to a pointer finally gets a compilation, but only one that generates a runtime memory exception!

    All in all, this is really annoying me. I suspect theres a gap in my knowledge here that is the underlying cause of all this. Probably to do with the pure virtual functions in my Graphic2D class.

    Simon

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    What compiler are you using? If you are using gcc [g++] (or some related product), you should be able to debug with gdb. Microsofts Visual Studio comes with a debugger too. If not one of those, let me know what compiler it is, and I will probably be able to give you some sort of hint.

    This is probably a good time to learn how to use a debugger - it will certainly help you in the future.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    You're trying out random things to see if they're working.

    The original problem is that you're creating a temporary rectangle and trying to bind it to a non-const reference. That's simply not allowed. I think the current problem come from a dead temporary.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed