Virtual functions

When a virtual function is declared in the Base class; does the output type have to be the same for each of the derived functions?

For example;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CPolygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area (void) =0;
  };

class CRectangle: public CPolygon {
  public:
    int area (void)
      { return (width * height); }
  };

class CTriangle: public CPolygon {
  public:
    int area (void)
      { return (width * height / 2); }
  };


Both of the area functions are of type int; and the virtual function is of the same type - no issue.

But say each object had co-ordinates for each vertex defining the shape and you wanted a virtual translate function - is it possible to return a different type dependant on when the function is called (for example; CTriangle.translate() would return a CTriangle object, CRectangle.translate would return a CRectangle object, etc.)?
They have to return the same type or a pointer/reference to a related type.
Why would "transtate" return anything at all though?
My initial plan was to store the result of the translation as a different object - so the user can see the coordinates before and after the operation.

So loosely speaking:
CTriangle Triangle - co-ordinates (0, 0), (1,0), (0, 1)
[Rotate 90 degrees counter clockwise about origin]
CTriangle Triangle_Res - co-ordinates (0, 0), (0,1), (-1, 0)
Where Triangle and Triangle_Res are two different instances of CTriangle.

This can't be done easily using Virtual functions, but by making it a void function which prints the coordinates after performing the necessary actions i.e.
Triangle - co-ordinates (0, 0), (1,0), (0, 1)
[Rotate 90 degrees counter clockwise about origin]
Output co-ordinates (0, 0), (0,1), (-1, 0)

Here only Triangle is an object belonging to CTriangle - the output coordinates either replace Triangles coordinates, or they are just printed to the screen (Guessing this is what you were thinking when you asked why return anything?)

Converting to the second case shouldn't be too much work; I'll just amend to allow that.

Thanks for your help
the clean way is to use a template function, if you intend to return the same type back from the transformation (due to the fact that the same transformation algorithm applies to all CPolygon, regardless of how many vertices you have, and yet, you want to maintain the original type).

technically, you could achieve the same result using virtual functions, but then you would have to downcast to get your original type back and downcasting is not a good thing (you could also make CRectangle, CTriangle call the common transformation function, but that results in a lot of boilerplate code).

conclusion: template functions are cleanest

here is a working example of a flip about the y-axis that you can easily adapt for your rotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <vector>

using namespace std;

struct CPoint {
	CPoint( int x, int y ) : m_x( x ), m_y( y ) { }
	int m_x, m_y;
};

class CPolygon {
	public:
		CPolygon( const vector<CPoint>& points ) : m_points( points ) { }
		CPolygon() { }
		void init( const vector<CPoint>& points ) { m_points = points; }
		const vector<CPoint>& getPoints() const { return m_points; }
		friend ostream& operator<<( ostream& os, const CPolygon& p );
	protected:
		vector<CPoint> m_points;
};
ostream& operator<<( ostream& os, const CPolygon& p ) {
	size_t i, n = p.m_points.size();
	for ( i=0; i<n; ++i )
		os << "(" << p.m_points[i].m_x << "," << p.m_points[i].m_y << ")" << endl;
	return os;
}

class CRectangle : public CPolygon {
	public:
		CRectangle( const vector<CPoint>& points ) :
			CPolygon( points )
		{ }
		CRectangle() { }
};

// TODO: write your rotateAboutOrigin using this as a model
template<typename P>
void flipAboutYAxis( const P& orig, P& result ) {
	vector<CPoint> newPts;
	const vector<CPoint>& origPts = orig.getPoints();
	size_t i, n = origPts.size();
	for ( i=0; i<n; ++i )
		newPts.push_back( CPoint( -origPts[i].m_x, origPts[i].m_y ));
	result.init( newPts );
}

main()
{
	vector<CPoint> points;
	points.push_back( CPoint( -10, -10  ));
	points.push_back( CPoint( -10,  20  ));
	points.push_back( CPoint(  15,  20  ));
	points.push_back( CPoint(  15, -10  ));

	CRectangle orig( points ), newRect;
	flipAboutYAxis<CRectangle>( orig, newRect );
	cout << newRect;
}

Last edited on
Something that throws me a bit about that piece of code is the size_t type declaration used there - couldn't see a definition, but from what I can gather it can be replaced with int with no loss of data.

Is there a particular reason it was chosen over int?

size_t is unsigned. That means it has no negative values, but its maximum value is about twice as high as a 32-bit int's.

-Albatross
Last edited on
Topic archived. No new replies allowed.