473,499 Members | 1,922 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

virtual functions & container interaction

Hi,

I've come upon a situation where I don't understand what's happening.
Basically, I'm implementing a kind of 'chain of responsibility' using
some covariant types:

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

class Shape {
public:
int sides;
};

class Square : public Shape {
public:
int sides;
Square() : sides(4) {};
};

class Triangle : public Shape {
public:
int sides;
Triangle() : sides(3) {};
};
class ShapeMaker {
public:
virtual Shape* CreateObject(void) const = 0;
};
class SquareMaker : public ShapeMaker {
public:
Square* CreateObject(void) const {
cout << "CreateObject: for SQUARE" << endl;
return (new Square);
}
};

class TriangleMaker : public ShapeMaker {
public:
Triangle* CreateObject(void) const {
cout << "CreateObject: for TRIANGLE" << endl;
return (new Triangle);
}
};

class ShapeContainer {
private:
vector<Square*squares;
vector<Triangle*triangles;
vector<Shape*shapes;
public:
void enter(Square* s) { squares.push_back(s); }
void enter(Triangle* t) { triangles.push_back(t); }
void enter(Shape* s) { shapes.push_back(s); }

friend ostream& operator<<(ostream& os, ShapeContainer &c) {
return os << "Container has \t" << c.squares.size() << " squares
\n"
<< " \t" << c.triangles.size() << "
triangles\n"
<< " \t" << c.shapes.size() << " generic shapes
\n";
}

};

int main ()
{

SquareMaker* sfactory = new SquareMaker;
TriangleMaker* tfactory = new TriangleMaker;

cout << "\nFirst Set of Tests:" << endl;
ShapeContainer container1;
container1.enter(sfactory->CreateObject());
container1.enter(tfactory->CreateObject());
cout << container1;

cout << "\n\nSecond Set of Tests:" << endl;
vector<ShapeMaker*master_factory;
master_factory.push_back(sfactory);
master_factory.push_back(tfactory);
ShapeContainer container2;

vector<ShapeMaker*>::iterator it = master_factory.begin();
while(it != master_factory.end()) {
// this calls the correct derived type function
// but returns an object of the base class. huh?
container2.enter( (*it)->CreateObject() );
++it;
}

cout << container2;
}

This compiles under gcc 4.0.1 (apple, x86). When I run it I get this:

First Set of Tests:
CreateObject: for SQUARE
CreateObject: for TRIANGLE
Container has 1 squares
1 triangles
0 generic shapes
Second Set of Tests:
CreateObject: for SQUARE
CreateObject: for TRIANGLE
Container has 0 squares
0 triangles
2 generic shapes

My question is why does (*it)->CreateObject() call the correct derived
class type, but result in a base class return type? Please be gentle;
I've been writing Perl code for years now and I'm lazy and used to
dynamic bindings.

TIA

Apr 5 '07 #1
2 1982
babaco wrote:
Hi,

I've come upon a situation where I don't understand what's happening.
Basically, I'm implementing a kind of 'chain of responsibility' using
some covariant types:

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

class Shape {
public:
int sides;
};

class Square : public Shape {
public:
int sides;
Square() : sides(4) {};
};

class Triangle : public Shape {
public:
int sides;
Triangle() : sides(3) {};
};
Why do you give Square and Triangle a member "sides" when this is
already included in Shape? Probably you mean to declare this in Shape only.
>
class ShapeMaker {
public:
virtual Shape* CreateObject(void) const = 0;
};
class SquareMaker : public ShapeMaker {
public:
Square* CreateObject(void) const {
cout << "CreateObject: for SQUARE" << endl;
return (new Square);
}
};

class TriangleMaker : public ShapeMaker {
public:
Triangle* CreateObject(void) const {
cout << "CreateObject: for TRIANGLE" << endl;
return (new Triangle);
}
};

class ShapeContainer {
private:
vector<Square*squares;
vector<Triangle*triangles;
vector<Shape*shapes;
public:
void enter(Square* s) { squares.push_back(s); }
void enter(Triangle* t) { triangles.push_back(t); }
void enter(Shape* s) { shapes.push_back(s); }

friend ostream& operator<<(ostream& os, ShapeContainer &c) {
return os << "Container has \t" << c.squares.size() << " squares
\n"
<< " \t" << c.triangles.size() << "
triangles\n"
<< " \t" << c.shapes.size() << " generic shapes
\n";
}

};

int main ()
{

SquareMaker* sfactory = new SquareMaker;
TriangleMaker* tfactory = new TriangleMaker;

cout << "\nFirst Set of Tests:" << endl;
ShapeContainer container1;
container1.enter(sfactory->CreateObject());
container1.enter(tfactory->CreateObject());
cout << container1;

cout << "\n\nSecond Set of Tests:" << endl;
vector<ShapeMaker*master_factory;
master_factory.push_back(sfactory);
master_factory.push_back(tfactory);
ShapeContainer container2;

vector<ShapeMaker*>::iterator it = master_factory.begin();
while(it != master_factory.end()) {
// this calls the correct derived type function
// but returns an object of the base class. huh?
container2.enter( (*it)->CreateObject() );
++it;
}

cout << container2;
}

This compiles under gcc 4.0.1 (apple, x86). When I run it I get this:

First Set of Tests:
CreateObject: for SQUARE
CreateObject: for TRIANGLE
Container has 1 squares
1 triangles
0 generic shapes
Second Set of Tests:
CreateObject: for SQUARE
CreateObject: for TRIANGLE
Container has 0 squares
0 triangles
2 generic shapes

My question is why does (*it)->CreateObject() call the correct derived
class type, but result in a base class return type? Please be gentle;
I've been writing Perl code for years now and I'm lazy and used to
dynamic bindings.
The problem you're running into is that functions can be virtual with
respect to their calling object but not with respect to their arguments.
Hence (*it)->CreateObject() looks statically like
ShapeMaker::CreateObject() which resolves dynamically as you expect.
However, in container2.enter( (*it)->CreateObject() ), the enter
function is resolved at compile time based on the static type of
(*it)->CreateObject() which is a Shape* (since the static type of **it
is ShapeMaker).

There are of course tricks to make functions appear virtual with respect
to their arguments. One way to do this is define foo( Obj& o) as
o.doSomethingTo( foo) where doSomethingTo is a virtual function of class
Obj.

-Mark

// simple example...
// C::foo is "virtual" with respect to its argument

#include <iostream>

using namespace std;

struct C;

struct B
{
virtual void apply( C& c) = 0;
};

struct D1 : public B
{
virtual void apply( C& c)
{
cout << "case D1" << endl;
}
};

struct D2 : public B
{
virtual void apply( C& c)
{
cout << "case D2" << endl;
}
};

struct C
{
void foo( B& b)
{
b.apply( *this);
}
};

int main()
{
C c;
B* b1 = new D1;
B* b2 = new D2;
c.foo( *b1);
c.foo( *b2);
}

Apr 5 '07 #2
On Apr 4, 6:24 pm, Mark P <use...@fall2005REMOVE.fastmailCAPS.fm>
wrote:
>
Why do you give Square and Triangle a member "sides" when this is
already included in Shape? Probably you mean to declare this in Shape only.

Yes, that's what I meant. I should have known I couldn't post code to
usenet without making a mistake ...
>
The problem you're running into is that functions can be virtual with
respect to their calling object but not with respect to their arguments.
Hence (*it)->CreateObject() looks statically like
ShapeMaker::CreateObject() which resolves dynamically as you expect.
However, in container2.enter( (*it)->CreateObject() ), the enter
function is resolved at compile time based on the static type of
(*it)->CreateObject() which is a Shape* (since the static type of **it
is ShapeMaker).
Thank you, that helps.

Apr 5 '07 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

16
7251
by: Geiregat Jonas | last post by:
I've seen some c++ code where before a method there was vitual ex: class foo{ public: virtual bool method(); } what does virtual do ?
4
4633
by: Sat | last post by:
Hi, I have a simplified version of a problem that I am facing (hope I haven't oversimplified it). This code doesn't work now and I want to find how I can make it work. Can I call the derived...
19
2309
by: qazmlp | last post by:
class base { // other members public: virtual ~base() { } virtual void virtualMethod1()=0 ; virtual void virtualMethod2()=0 ; virtual void virtualMethod3()=0 ;
9
5000
by: richard.forrest1 | last post by:
I have a problem with an abstract interface class whose implementation classes need to return different iterator types (but with the same value_types etc). Classes A and B both conform to the...
4
4192
by: WittyGuy | last post by:
Hi all, Though I know the concepts of both abstract class & virtual function (like derived class pointer pointing to base class...then calling the function with the pointer...), what is the real...
15
2091
by: christopher diggins | last post by:
I posted to my blog a special pointer class work-around for inheriting from base classes without virtual destructors. I was wondering if there is any other similar work, and whether there are any...
5
1522
by: richard pickworth | last post by:
What does "virtual" mean? thanks richard
5
1968
by: toton | last post by:
Hi, I want a few of my class to overload from a base class, where the base class contains common functionality. This is to avoid repetition of code, and may be reducing amount of code in binary,...
7
2099
by: Markus Svilans | last post by:
Hello, My question involves virtual functions and inheritance. Suppose we have a class structure, that consists of "data" classes, and "processor" classes. The data classes are derived from...
0
7007
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7174
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
1
6894
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
5470
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
1
4919
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
4600
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
3099
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3091
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
665
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.