Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

inheritance and that sort of thing 1

Status
Not open for further replies.

LyMc

Programmer
Jun 3, 2003
84
US
I've got a pure C++ theory question, should be a pop-fly for y'all out here.

I have a library that builds a tree-form data structure from an input file. For example, it reads in a "PlanSet" which contains a number of "Plan"s, which in turn have their own components. The relevant classes look like this:
Code:
	class PlanSet {
		Plan* plan1;
		Plan* plan2;
		...
	public:
		PlanSet();
	};
	
	PlanSet::PlanSet() : plan1 (NULL), plan2 (NULL) {}

	class Plan {...};
	
	Plan::Plan() {}

OK, now I want to use the library in an application that works on the tree. I need to capture some intermediate results in PlanSet and in Plan, so in the app I add these classes:
Code:
	class PlanSetEx : public PlanSet {
		Object* whatever;
	public:
		PlanSetEx();
	};
	
	PlanSetEx::PlanSetEx() : whatever (NULL) {}
	
	class PlanEx : public Plan {<other stuff>};
	
	PlanEx::PlanEx() : <init_other_stuff> {}

Now, this is ok, the PlanSetEx constructor calls the PlanSet constructor, and everything works ok with casts in the app to say that all PlanSet pointers are really PlanSetEx pointers. The problem is that the import function down in the library, which is reading the input and creating the tree, is creating PlanSets and Plans, not PlanSetExs and PlanExs. The crunch comes when I access the whatever pointer in the app, and I get back an uninitialized value because the PlanSetEx initializer isn't called when the tree is built.

Now, I know I could copy the PlanSet import function out of the library into the PlanSetEx implementation and have it call the right constructors, but that certainly is not in the spirit of inheritance as I understand it. How should I be organizing the classes so that I can replace a part of the lower level library class with the upper level app one, and the app specific stuff stays up there while the linkage and universal functionality stays in the library?
 
No - can't say that I do. Please explain.
 
How do you tell the import function that it should instantiate ...Ex classes?

/Per

if (typos) cout << &quot;My fingers are faster than my brain. Sorry for the typos.&quot;;
 
Well now, that's the question, isn't it? The import function (not to be confused with obislavu's import classes, I think) are in the library. They normally instantiate the &quot;normal&quot; classes as they build the tree. I would like them to be able to instantiate, instead, the Ex classes, but they don't exist in the library, only in the app (there may be several apps, each with its own, different extensions).
 
Can you provide some kind of class factory to the import function that it can use? Ie a class you design that creates new classes. If not, then I dont know you could let the import function instantiate classes its never heard about.




/Per

if (typos) cout << &quot;My fingers are faster than my brain. Sorry for the typos.&quot;;
 
1. what do you mean import/export,
is it a __declspec(dllimport)?
2. how are you instantiating your PlanSet and want to get a PlanSetEx? I'm just confused.

Ion Filipski
1c.bmp

ICQ: 95034075
AIM: IonFilipski
filipski@excite.com
 
Well, don't feel like you're alone - I'm confused here too! The &quot;import&quot; that I'm referring to in the original posting is the process of reading in the specification and building the in-core tree structure. I'm not sure what import obislavu is referring to. I'm not using dlls at this point; the library is a static load-time library, and I know about the issues concerning dll import/export - I don't think they're relevant to this discussion.

Right now I'm instantiating Planset and Plan in the library during my import function. It would appear from the discussion here that I will have to add the additional data that I need in the apps into the core classes so it may be initialized properly when an item is constructed in the library, even if it is not used at all by the library itself. I had thought there might have been a way for the app to say to the library, &quot;Here is an extension to the PlanSet class; use it instead of (or rather, in addition to) the more basic PlanSet class itself in this application, **along with these constructor/destructor extensions**.&quot; I can certainly extend the class, but I can't figure out how to force recognition of the extended constructor in the library.
 
I believe you shoud invent some reflection mechanism. Such mechanism exist in Java for example, but in C++ I think is hard to accomplish. The only way, is you to find out a way to Plug your application In that library, or something like that.

Ion Filipski
1c.bmp

ICQ: 95034075
AIM: IonFilipski
filipski@excite.com
 
You could extend the import function, right?

Here's an example of a simple class factory. The import can instantiate PlaneEx without knowing it. It must know the interface of a factory, but that still only shows Plane*.
Code:
class PlaneFactory
{
public:
  PlaneFactory(bool ex):mEx(ex) {}
  Plane* create() const
  {
     if(mEx)
       return new PlaneEx();
     else
       return new Plane();
  }
private:
  const bool mEx;
};
...
#include &quot;PlaneFactory.h&quot;

  theImport(const PlaneFactory& factory)
  {
     ...
     Plane* p = factory.createPlane();
  }
...
  PlaneFactory f(true);
  theImport(f);

...or perhaps pass a parameter to the create method it can use to decide on what to instantiate (and/or pass to the constructor).

/Per

if (typos) cout << &quot;My fingers are faster than my brain. Sorry for the typos.&quot;;
 
Thanks to all the replies on this. I have a proposal I would like you to review.

Suppose I made the Import function in the library a virtual function. In the app, I make the derived class Import call the base class Import (see the next paragraph). In the application I declare the class PlanSetEx with a constructor that initializes its own variables, overrides the Plan variables with PlanEx copies and calls the PlanSet constructor to initialize the base class. Since the main planset variable is of type PlanSetEx, Importing its overridden Plan members in the base class code would call the PlanEx constructor, would it not? Is there any problem with this approach (besides, of course, the confusion)? This seems to be what virtual classes are meant to do, but I have no feel for them as yet - one of the pitfalls in learning this stuff the experiential way.

It seems critical for me to be able to call the base class Import from the derived class Import if I'm to have the common import code in the base class, but none of the books I've consulted seem to allow for this - how exactly would it be done? I can't simply:
Code:
   PlanSetEx* plansetex = new PlanSetEx();
   ...
   ((PlanSet*) plansetex)->Import();
from within the PlanSetEx Import code, as if it were a simple override, can I? The books I've read seem to imply that the virtual Import does the whole import of both derived and base class itself (or rather, the examples leave the base class with no variables to be acted upon, only derived ones), but that would mean massive duplication of code from the library into the app. Of course, it could call the base class Import as a garden variety function, not a PlanSet class member, but it would have to use accessor functions all over the place. Is that the way to do it?

I'm groping here.
 
Well, I think the thing to decide is what to instantiate (for example by using a class factory as I mentioned). Once that is settled and you've instantianted the proper class just call its (possibly virtual) import. The point is the functaion calling the import only sees the PlanSet interface and doesnt have to care about what kind of instance it really is.

Seems a bit awkward instantiating a PlanSetExand then forcefully (by using casting) pretend it to be a PlanSet. Either it is a PlanSet, then the PlanSet's import should be called or it is a PlanSetEx and then the PlanSetEx's import should be called (not that the caller would know the difference).

I think it'd be much cleaner to just regard it as a PlanSet, and let the instantiation (and possible virtual method) deal with the internals. Consider you'd wish to add yet another class, PlanSetEx2. You'd then only change the internals of factory's create method.

/Per

if (typos) cout << &quot;My fingers are faster than my brain. Sorry for the typos.&quot;;
 
OK - I solved my problem, and in the end it was rather simple. Along the way I learned a bunch about virtual functions, which I'd been meaning to but hadn't got around to.

Following up today on what I'd typed earlier, I decided to try out making Import a virtual function. It works out that that is wrong, because it would involve copying the Import code to the app, which I didn't want. Then it occurred to me that it wasn't the import I wanted to be virtual, it was the class that the app's PlanSetEx class pointed to. But you can't make pointers virtual. Ah, but you can make the producer of the class virtual, and have it produce the right type. So in PlanSet I added:

Code:
PlanSet, PlanSetEx.h:  virtual Plan* crePlan();

PlanSet.cpp:
     Plan* PlanSet::crePlan() { return new Plan; }
     (replace all &quot;new Plan&quot; in the library with
      crePlan()).

PlanSetEx.cpp:
     Plan* PlanSetEx::crePlan() { return new PlanEx; }

The main program starts out with PlanSetEx* p = new PlanSetEx;, and from there on all creations are done with the Ex methods, the right constructors are called, and all is rosey.

I went upstairs to do the dishes, nearly breaking my arm patting myself on the back to congratulate myself on my cleverness. While working on a tough cassarole` pan it occurred to me that I was manufacturing classes, just like Per had suggested. I hadn't really understood what his posts were about until I had the answer in hand. So much for my originality.

Many thanks to Per, Ion and obislavu for their help; their prodding is what I needed. And, of course, thanks to Tecumseh for this forum. BTW, if anyone else needs help with virtual finctions, I'd encourage them to look at and Bjarne Stroustrop's FAQ at They were immensely helpful.

Thanks again, guys. :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top