CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 15
  1. #1
    Join Date
    Mar 2009
    Posts
    7

    Templates and Subtypes via inheritance

    Hi,

    I'm using g++ 4.3.2. I have templated base class, an inherited subclass with specific template types, and I'd like to subclass that type further with subtypes of the template types.. but I can't see how.

    In code:

    Code:
    class A { /* ... */ };
    
    class B : public A { /* ... */ };   // B is a subtype of A
    
    class C : public B { /* ... */ };   // C is a subtype of B
    
    template <class T>
    class BaseClass
    {
       // note, not a list of T* -- I want to avoid using dynamic memory
       vector<T> listOfStuff; 
    };
    
    class Subtype : public BaseClass<A>
    {
       // ... 
    };
    
    class YetAnotherSubtype : public Subtype
    {
      // in here I would like to have access to an inherited 
      // listOfStuff<B>
    
      // I have lots of code in Subtype class that I want to reuse that
      // acts on listOfStuff, but in here I want them to act on B's not A's
    };
    
    // I might even want to keep going further, eg
    // class Subtype3 : public YetAnotherSubtype { /*... */ }
    // that has listOfStuff<C> and still reuses code from Subtype
    There are performance reasons specific to my use of this code for avoiding declaring listOfStuff<T*> and using pointers and dynamic memory. If I have to I may, but I'd prefer not to.

    It would be OK to have the list structure declared dynamic, eg vector<T>* listOfStuff, if that will help let me achieve the subtype relationship I want.

    Any ideas?

  2. #2
    Join Date
    Nov 2008
    Location
    England
    Posts
    748

    Re: Templates and Subtypes via inheritance

    I take it A,B and C have virtual functions??

    Are you abusing inheritance for code reuse?

    If the classes have virtual functions then listOfStuff should be a list of A* and shouldn't need a template base class but if you are abusing inheritance I think redesigning might be the way to go. Hard to say for sure from whats shown, we've no idea whats behind the ....

    As for the class YetAnotherSubtype, you cant quite do what you want. If you inherit from subtype then you inherit a list of A. To get a list of B you would need inherit from BaseClass<B>. And Subtype3 would need inherit from BaseClass<C>.

    The more I look at this the more im convinced you are abusing inheritance. Could you maybe show a bit more complete example of classes A,B,C and how you intend to use them in the subtype classes.
    Get Microsoft Visual C++ Express here or CodeBlocks here.
    Get STLFilt here to radically improve error messages when using the STL.
    Get these two can't live without C++ libraries, BOOST here and Loki here.
    Check your code with the Comeau Compiler and FlexeLint for standards compliance and some subtle errors.
    Always use [code] code tags [/code] to make code legible and preserve indentation.
    Do not ask for help writing destructive software such as viruses, gamehacks, keyloggers and the suchlike.

  3. #3
    Join Date
    Mar 2009
    Posts
    7

    Re: Templates and Subtypes via inheritance

    Yes, A, B, and C ave virtual functions. That's the point. I'd like listOfStuff<B> to be a sub-type of listOfStuff<A>. In Java you can do that, why wouldn't you be able to in C++? Or are you saying I can do that if listOfStuff uses pointers rather than static object?

    Even if I have to make them dynamic.. I'd like the base classes to have a pointer to a structure of a generic type (say A) and I'd like the methods to operate on that type without regard for what its run-time type is (say B or C). The virtual functions are overridden so that the code re-use is generically applicable to all subtypes of A.

    Actually, I tried a test example.. this doesn't even work:

    Code:
    // fails to comple, even though B is a subclass of A
    vector<A*> * list = new vector<B*>;
    It could be because I'm thinking of templates in C++ as analogues to generics in Java but that may not be true -- in C++, polymorphism seems to work differently when writing generic code.

    I'm OK with re-designing, and I can better describe what is in those classes, but first I'd like a clear definition of "abusing inheritance" ... it would help to know what I'm doing wrong.
    Last edited by lanctot; March 11th, 2009 at 07:38 PM.

  4. #4
    Join Date
    Apr 1999
    Posts
    27,449

    Re: Templates and Subtypes via inheritance

    Quote Originally Posted by lanctot View Post
    I'd like a clear definition of "abusing inheritance" ... it would help to know what I'm doing wrong.
    "Abusing inheritance" could mean that in reality, those classes are not related in any real-world sense way, i.e. the Liskov Substitution Principle. If you're using inheritance just to save typing, and the parent and subtype really have no relationship to each other, then that could be considered abuse of inheritance.

    In a nutshell, suppose there are two classes, Elephant and Car, and the internal functions look similar. Are you deriving Elephant from Car just because Car and Elephant have similar functions, and you just need to tweak the Elephant version of those functions? If so, that could be considered abusing public inheritance. An Elephant is not a Car (the IS-A principle in OO programming for public inheritance).

    Instead of doing this with virtual functions, there is another paradigm in C++ programming called policy-based design. This also uses templates, but the resolution of what each type does is done at compile-time, not runtime. I don't know if Java generics can do this, but C++ can do this. Here is the link:

    http://en.wikipedia.org/wiki/Policy-based_design

    A small example of policy-based design is the std::string class. Note that std::string is really a typedef:
    Code:
    typedef basic_string<char, char_traits<char> > string;
    The char_traits<char> is a template that includes a set of functions that the "base class" basic_string uses to determine how to do certain operations, given that the template type, char, is given. Instead of hardwiring into the basic_string template a way to do stuff with char, we send another template class telling basic_string how to behave when char is the template type.

    Using your approach, you would have created a base class called "basic_string", then derived different ways to do char, then derived again using unsigned int (for wide string), etc. That would be one approach, but that requires virtual functions and runtime knowlege of the type. The policy -based approach resolves things at compile time, and no virtual functions are used.

    Not knowing your entire design or what your real goal is, maybe policy based programming is one way to accomplish what you're doing.

    Regards,

    Paul McKenzie

  5. #5
    Join Date
    Mar 2009
    Location
    Bulgaria
    Posts
    63

    Re: Templates and Subtypes via inheritance

    @lanctot, messing up the Java generics and the C++ templates is a mistake. In C++ writing std::list<A> simply substitutes the generic type with A at compile time and actually creates a list of real As. If you then have std::list<B> this creates another list of real Bs. So the compiler really generates these two separate lists - one from A and one from B and you invisibly have them in your source code. That's not what happens in Java. All this work there is performed at runtime. What you get when you want to have a container of As is practically a container of references to Object that are instantiated to A and all the function calls are performed polymorphically. Well, the compiler makes some check ups whether the concrete classes really have these methods, fields and so on but you get the idea. This has its own advantages and disadvantages. For example, in Java you can get runtime errors which is awful. Also, the runtime work is slower because of all the virtual stuff and other work going on behinds the scene. But, for example, if you have two completely different classes, you can put them in the same container, as long as they have the appropriate interfaces. This cannot be done in C++ because of the absence of a class like Object.

    You have enough answers on your subject, but let me explain what you can do. First of all, yes, you cannot have polymorphism through concrete objects. You should use pointers or references instead. In this case - pointers. It's even better to use smart pointers here (but please don't try std::auto_ptr, because it's a bad idea to put auto_ptrs in standard containers). So you can create a container of smart pointers to the base class and instantiate them to the derived class. That's the solution if you have an appropriate design (-:
    Last edited by yzaykov; March 12th, 2009 at 04:20 AM.

  6. #6
    Join Date
    Jul 2002
    Location
    Portsmouth. United Kingdom
    Posts
    2,727

    Re: Templates and Subtypes via inheritance

    I'd like listOfStuff<B> to be a sub-type of listOfStuff<A>. In Java you can do that, why wouldn't you be able to in C++?
    Because in Java references are effectively pointers under the hood. To achieve a similar thing in C++ therefore requires the use of pointers.
    "It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
    Richard P. Feynman

  7. #7
    Join Date
    Mar 2009
    Posts
    7

    Re: Templates and Subtypes via inheritance

    Thanks for all the responses! This is a great forum to have when I need help with C++. As you may tell I've been using Java for the past 5 years and so now re-learning C++ .. last time I'd used it was in 95 or so.

    Ok so the reason I didn't want pointers and dynamic allocation if possible was because I'm creating and destroying lots of these small objects.. I wanted to avoid the slowdown associated with allocation and deallocation of memory. I was told that SOAs and pools are the way to go for this.. so I looked into what those are. I'm doing a depth-first tree traversal where I need to create the node below and use it to keep traversing, but only while it traverses the subtree under that node (the trees are huge, not able to fit in memory on their own.) Turns out it was easy enough to make my own Pool that was quite efficient; the boost:bject pool didn't suffice because its deallocate is O(N) ... all my objects are being allocated and deallocated as a stack and none contain dynamic information so I don't need the call any destructors, so I could easily do deallocate by popping off the stack and reusing the memory. So now what I'm doing is using a pool<B*> to create the object but keeping a stack of A* in the base classes -- and the code isn't slowed because the pool data structure makes sure that allocation and deallocation are efficient for my purpose.

    Paul: so great, I'm not abusing inheritance then in this case, it's just that for what I wanted to do I needed dynamic memory to use polymorphism. With regard to my explanation above, sometimes I would like to have a sub-class of these nodes that contains most of the code in the node with a slight tweak. So it's nice not to have to re-write all the other functions, just the one that needs tweaking and then plug it back in. The policy-based approach you mention seems more efficient than inheritance via virtual functions, and performance is important in my case, so when I have the time maybe I will switch everything over to the policy-based design. Thanks for the links.

    JohnW and yzaykov: I violated one of the C++ OO commandments: Thou shalt not confuse Java generics with C++ templates. I found a different way around my problem that avoids templates altogether, so it's no longer an issue. However, I thought I could still treat vector<B*> as a subtype of vector<A*>. This web site implies that you can't:

    http://www.research.att.com/%7Ebs/bs...tml#conversion

    ... but a friend of mine told me you can with using a cast (dynamic or reinterpret?); I haven't verified that to be true yet. It seems weird to me because I'm used to thinking of templates as generics: any operation that you could perform on a collection of animals you should be able to perform on a collection of, say, cows. It's because I'm used to Java doing dynamic type-checking at run-time (in the example from that page the failure would occur at runtime.. maybe that's what casting allows you to defer.)

    Thanks again!

  8. #8
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Templates and Subtypes via inheritance

    Quote Originally Posted by lanctot
    JohnW and yzaykov: I violated one of the C++ OO commandments: Thou shalt not confuse Java generics with C++ templates.
    Heheh, but generic programming is a different paradigm from object oriented programming, though they have things in common

    Quote Originally Posted by lanctot
    It seems weird to me because I'm used to thinking of templates as generics: any operation that you could perform on a collection of animals you should be able to perform on a collection of, say, cows.
    That is true if the operation is on the contents on the collection. It is also true with C++. What is not correct in both C++ and Java is to treat the generic containers/collections polymorphically just because their contents are polymorphic. That is, just because A is-a B does not mean that a collection of A is-a collection of B.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  9. #9
    Join Date
    Mar 2009
    Posts
    7

    Re: Templates and Subtypes via inheritance

    Just for completion I tried out the subtyping relationship I mentioned in my last post. It fails using a dynamic_cast, which I'm puzzled by.. but it works with reinterpret_cast (code below).

    I thought dynamic_cast was for this purpose, and I thought it would either issue errors at run-time or cast down to 0 if there's a run-time error. So we're certain that vector<Cow*> is not a subtype of vector<Animal*> ? But reinterpret cast lets you do it. Why?

    Code:
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Animal
    {
    public:
      virtual void claim()
      { cout << "I'm an animal!" << endl; }
    };
    
    class Cow : public Animal
    {
    public:
      virtual void claim()
      { cout << "I'm a cow!" << endl; }
    };
    
    void groupClaim(vector<Animal*> * group)
    {
      for (int i = 0; i < group->size(); i++)
        group->at(i)->claim();
    }
    
    int main()
    {
      //vector<Animal*> * animals = new vector<Cow*>; // (fails)
      //vector<Animal*> * animals = dynamic_cast<vector<Animal*>*>(new vector<Cow*>); // fails
      vector<Animal*> * animals = reinterpret_cast<vector<Animal*>*>(new vector<Cow*>); // "works"
    
      animals->push_back(new Cow);
      animals->push_back(new Cow);
      animals->push_back(new Cow);
    
      groupClaim(animals);
    }
    Prints out "I'm a cow!" three times.
    Last edited by lanctot; March 12th, 2009 at 11:16 AM.

  10. #10
    Join Date
    Mar 2009
    Posts
    7

    Re: Templates and Subtypes via inheritance

    Quote Originally Posted by laserlight View Post

    That is true if the operation is on the contents on the collection. It is also true with C++. What is not correct in both C++ and Java is to treat the generic containers/collections polymorphically just because their contents are polymorphic. That is, just because A is-a B does not mean that a collection of A is-a collection of B.
    So, why not? I think I ran into this problem when I was coding a generic subclass in Java and just expected for it to be true.. I don't remember the explanation.. was it only because of the example above? Eg. that you don't want to modify the collection by adding subtypes which are incompatible with the actual content type at runtime? In that case, shouldn't const collections still be polymorphic?

  11. #11
    Join Date
    Mar 2009
    Location
    Bulgaria
    Posts
    63

    Re: Templates and Subtypes via inheritance

    You don't have to do this cast.

    Code:
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class Animal
    {
    public:
      virtual void claim() const
      { cout << "I'm an animal!" << endl; }
    };
    
    class Cow : public Animal
    {
    public:
      virtual void claim() const
      { cout << "I'm a cow!" << endl; }
    };
    
    void groupClaim(const vector<Animal*>& group)
    {
      for (int i = 0; i < group.size(); i++)
        group[i]->claim();
    }
    
    int main()
    {
      vector<Animal*> animals;
    
      animals.push_back(new Cow);
      animals.push_back(new Cow);
      animals.push_back(new Cow);
    
      groupClaim(animals);
    
      // you are not using smart pointers, so !!!FREE!!! memory here
    }

  12. #12
    Join Date
    Mar 2009
    Posts
    7

    Re: Templates and Subtypes via inheritance

    Got it.

    OK, FYI it has nothing to do with constness because I just tried it and it works with and without the const. The idea is that the collection/container not be a subtype but to hold pointers to polymorphic types in the container.

    Thanks again!

  13. #13
    Join Date
    Mar 2009
    Location
    Bulgaria
    Posts
    63

    Re: Templates and Subtypes via inheritance

    I still sometimes come across programmers who think const isn't worth the trouble. "Aw, const is a pain to write everywhere," I've heard some complain. "If I use it in one place, I have to use it all the time. And anyway, other people skip it, and their programs work fine. Some of the libraries I use aren't const-correct either. Is const worth it?"

    We could imagine a similar scene, this time at a rifle range: "Aw, this gun's safety is a pain to set all the time. And anyway, some other people don't use it either, and some of them haven't shot their own feet off…"
    -- Herb Sutter
    Last edited by yzaykov; March 13th, 2009 at 03:18 AM.

  14. #14
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,765

    Re: Templates and Subtypes via inheritance

    Quote Originally Posted by yzaykov
    you are not using smart pointers, so !!!FREE!!! memory here
    Speaking of that, Animal should have a virtual destructor.

    EDIT:
    Heh, and it is more correct to use vector<Animal*>::size_type instead of int in implementing groupClaim(), but then we could just #include <algorithm> and <functional> and write:
    Code:
    for_each(group.begin(), group.end(), mem_fun(&Animal::claim));
    Last edited by laserlight; March 13th, 2009 at 03:43 AM.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  15. #15
    Join Date
    Jul 2002
    Location
    Portsmouth. United Kingdom
    Posts
    2,727

    Re: Templates and Subtypes via inheritance

    I get annoyed with myself when I've inadvertently forgotten const correctness. I am so cross, I won't speak to myself for hours.
    "It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
    Richard P. Feynman

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured