goss@ese.essex.ac.uk (Gossain Sanjiv (5N.7.10)) (05/04/89)
I have a particular application where I am representing a group of related classes by an abstract class. Now, I require that another object of another class, is passed an instance of one of these classes as a parameter in one of its methods. I would like to be able to declare the abstract class as the type in the functiion declaration, as I won't know until run time, which class of my group it will be. Are you confused? Let me explain with some code... I would like a function of class X ( X::A) to have the decl: void X::A(abstractClass* aMemberOfAbsClass) now, aMemberOfAbsClass could be any instance of a sub-class of abstractClass. X::A should be able to call a member function of aMemberOfAbsClass that is not defined for abstractClass, should I ---OPTION 1 ---- (1) Define an empty function with the same name as for the sub-class in the base-class's declaration. e.g. class Y { int a; public: Y(int dd) { a = dd; } virtual int get_both() { } }; class Z : public Z { int y; public: Z(int dd, int gg) : (dd) { y = gg; } int get_both() { return y*Y::get(); } }; and then use ... main() { ... ... Y* someZ = new Z(2,3); someX->A(someZ); ... } with get_both() being used in the body of X::A ... void X::A(Y* someYorZ) { //.. someYorZ->get_both(); } --------------- Or (2) should I do some sort of casting operation? (I hope the question is clear) Which is the better practice, or is there some other alternative. Any help from C++ gurus gladly appreciated. -- Sanjiv ****************************************************************** Sanjiv Gossain | goss%ese.essex.ac.uk@nsfnet-relay.ac.uk Dept. of ESE | Tel: +44 206 873333 Ext. 2820 University Of Essex Colchester CO4 3SQ ENGLAND ******************************************************************
rfg@riunite.ACA.MCC.COM (Ron Guilmette) (05/04/89)
In article <897@servax0.ESSEX.AC.UK> goss@ese.essex.ac.uk writes: { and I paraphrase ... } {Given that I have a pointer to an object, and given that the object is itself (at least) of some particular base class, I would like to...} >... be able to call a member function of {the actual derived class type of the object} >that is not defined for {the obvious static base class type of the object}, should I > >---OPTION 1 ---- > >(1) Define an empty function with the same name as for the sub-class > in the base-class's declaration. ... >Or > >(2) should I do some sort of casting operation? I would also like an answer to this question. What is stylistically preferable? Is there a consensus on which of these alternatives is better? -- // Ron Guilmette - MCC - Experimental Systems Kit Project // 3500 West Balcones Center Drive, Austin, TX 78759 - (512)338-3740 // ARPA: rfg@mcc.com // UUCP: {rutgers,uunet,gatech,ames,pyramid}!cs.utexas.edu!pp!rfg
shopiro@alice.UUCP (Jonathan Shopiro) (05/04/89)
In article <897@servax0.ESSEX.AC.UK>, goss@ese.essex.ac.uk (Gossain Sanjiv (5N.7.10)) writes: [slightly edited] > I have a particular application where I am representing a group > of related classes by an abstract class. Now, I require that > a function is passed an instance of > one of these classes as a parameter in one of its methods. > I would like to be able to declare the abstract class as the > type in the function declaration, as I won't know until > run time, which class of my group it will be. Are you > confused? Let me explain with some code... > > I would like a function to have the decl: > > void f(abstractClass* anInstanceOfAbsClass) > > now, anInstanceOfAbsClass could be any instance of a sub-class > of abstractClass. > > f should be able to call a member function of anInstanceOfAbsClass > that is not defined for abstractClass, should I > There are (approximately) two cases: 1) There is some function that will be implemented in every derived class, but it has different implementations in each. This is exactly what virtual functions were invented for. Here's how: class abstractClass { // ... public: virtual void function() = 0; // a ``pure virtual'' function }; The initializer is new syntax with 2.0. It has several effects: 1) you don't need an implementation for the base class 2) you must redeclare it in each derived class (possibly also as pure virtual) 3) you can only create objects of the derived classes If you call the function directly or indirectly in the constructor of the base class, something bad will happen, but as soon as the base class constructor completes, all will be well. 2) There is some function that is only appropriate for some of the derived classes. This case is more problematical. What you should look at is how you might decide to call the function. Perhaps the function is a sub- operation of some function that is defined for all the derived classes. Then you can just put it in the classes where it is appropriate. class Base { // ... public: virtual void function() = 0; }; class Derived : public Base { public: void function(); void special(); }; void Derived::function() { // ... special(); // ... } Here ``function'' will be defined in all derived classes, and ``special'' is not defined in all derived classes. This is the most natural way to use C++, and you should try to structure your programs in this way. I am sure you will like the result. If this is not possible, you have two choices. First, you can put the special function in only the class where it is appropriate and then use a cast to call it. Using the declarations above, you could write void f(Base* bp) { // decide that bp really points to a Derived object, and you // want to call special ((Derived*)bp)->special(); } This is guaranteed to work only if bp really does point to a Derived object. If the special function will be defined in several derived classes, but not all, you can create an intermediate class, derived from Base, which has special as a pure virtual function, and then derived the classes which will implement special from that. There is no performance penalty for extra classes in a derivation hierarchy. The other choice is to put special in the Base class, as a virtual function whose implementation calls some error routine, and to override it in the appropriate derived classes. The advantage of this approach is that you get to define the behavior in case of error, and the advantage of the other approach is that each class has only appropriate member functions. Personally I prefer the first approach, with casting. -- Jonathan Shopiro AT&T Bell Laboratories, Warren, NJ 07060-0908 research!shopiro (201) 580-4229
johnson@p.cs.uiuc.edu (05/05/89)
Given the choice of 1) defining a virtual function with a null body in the base class and 2) casting a pointer I would ALWAYS pick 1. One of the characteristic features of an abstract class is that it relies on code that is defined by subclasses. Virtual methods provide this mechanism in C++. Abstract classes are one of the key ideas in object-oriented design and the most important functions in an abstract class are usually the ones that are defined in the subclasses. An abstract class essentially contains the instructions for deriving subclasses from it, because you just define the functions that are left to the subclasses. C++ requires that virtual functions be defined in the base class, so you have to provide a dummy body or the loader will complain. This idea is certainly not specific to C++, but is a feature of all object-oriented languages. In Smalltalk, the base of most large class hierarchies is abstract, such as Collection, Number, and DisplayObject. When it isn't, such as class View, it usually should be. Ralph Johnson
lsr@Apple.COM (Larry Rosenstein) (05/06/89)
In article <897@servax0.ESSEX.AC.UK> goss@ese.essex.ac.uk (Gossain Sanjiv (5N.7.10)) writes: > I have a particular application where I am representing a group > of related classes by an abstract class. Now, I require that A good thing to do, assuming you haven't already, is to make the constructor for the abstract class protected. Then clients cannot create instances of the abstract classes, only of the concrete classes (which have public constructors). Larry Rosenstein, Apple Computer, Inc. Object Specialist Internet: lsr@Apple.com UUCP: {nsc, sun}!apple!lsr AppleLink: Rosenstein1
mat@mole-end.UUCP (Mark A Terribile) (05/06/89)
> I have a particular application where I am representing a group of > related classes by an abstract class. Now, I require that another > object of another class, is passed an instance of one of these > classes as a parameter in one of its methods. I would like to be > able to declare the abstract class as the type in the function > declaration, as I won't know until run time, which class of my group > it will be. Are you confused? Let me explain with some code... A little. Isn't this what derived types are all about? If you want other classes to interpret instructions originally designed for one class, you derive the other classes from it. You are limited for a little while longer to single inheritance, so you may have problems if you have something else you want for a base class. When Multiple Inheritance comes along, this restriction will be lifted. > I would like a function of class X ( X::A ) > void X::A( abstractClass* aMemberOfAbsClass ) > now, aMemberOfAbsClass could be any instance of a sub-class > of abstractClass. Eh? I think you mean InstanceOfAbsClass ... > X::A should be able to call a member function of aMemberOfAbsClass > that is not defined for abstractClass, should I > (1) Define an empty function with the same name as for the sub-class > in the base-class's declaration. Absolutely. That's what virtual functions are for. Provide yourself as many empty virtual functions as you need in the base class. This ensures that any call is legal for any derived object. > class Y { > int a; > public: > Y(int dd) { a = dd; } > virtual int get_both() { } > }; > > class Z : public Z { > int y; > public: > Z(int dd, int gg) : (dd) { y = gg; } > int get_both() { return y*Y::get(); } > }; I don't see a get() in the declaration of Y() . If we assume it's there, you only need the qualifier Y:: if class Z declares a get() of its own. > (2) should I do some sort of casting operation? I hope you're not seriously thinking of this! There's a mechanism provided that doesn't violate the type structure. Because of the way that pointers to virtual functions are implemented, a really well botched miscast could do all sorts of bad things. -- (This man's opinions are his own.) From mole-end Mark Terribile