Thread: Passing reference to derived class, where base class is expected

  1. #16
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    Thanks,
    so at the moment I cant think of a situation, but its more about future design, as I wanted to design the Base classes so they sere inherited from.
    If the baseGraph is the only thing that knows of all the nodes in the graph in the vector, then that seems fine to me.
    But if a user inherited twice say from BaseNode and created two different types of DerivedNode, then added them both to the vector - and gave them each specific functions that were not overriding functions in BaseNode, then they could not just iterate over the vector and run those functions as not all nodes would have all methods.
    So my question is, is the bad design that they are adding different types of nodes to the vector owned by graph, or that I am storing all nodes that derive from BaseNode in a vector of BaseNodes?

    Secondly, what is the way to handle that - i.e if there are two types of DerivedNode and the graph has a list of all nodes, but the user wants to process al DerivedNodeType1 nodes, then should they be adding them to their DerivedGraph class as another vector for each type, or should they be checking the type of a node in the vector of BaseNode types in BaseGraph?

    Thinking about it, they should be storing their own vector of their own types of nodes if they need to treat them differently. It just doesnt feel too generic - by that I mean, I want BaseGraph to have a list of all nodes in existence, but should the DerivedGraph have the granular knowledge of different types of Node?

  2. #17
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    Oh and when I do things like getNeighbours(), if I return a vector on the stack of pointers then I obviously dont have to destroy the vector, however IF, and I know its wrong (now), but IF I was doing this:
    Code:
    vector<Node*> *nodes;
    nodes = new vector<Nodes*>;
    graph.getNeighbours(node, nodes);//adds all nodes that are connected to node to the vector nodes
    The reason I have thought I need to pass it in, is because I thought I need to destroy it from here, as I cant destroy it inside the funcftion - otherwise I would lose everything I am after.
    So Can I do:
    [CODE]
    Code:
    vector<Node*> *nodes;
    nodes = graph.getNeighbours(node);//returns a list of all the nodes
    if graph.getNeighbours() in this situation returned a vector on the HEAP of the nodes attached to node, would I then be able to destroy it from here, even though it was created inside getNeighbours()? That seems like an odd question possibly, but it gave me a bit of a head ...........
    Thanks guys!

  3. #18
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    As long as you are dealing with standard C++, it does not matter where you allocate and where you deallocate your memory. Well, as long as you don't try to access the deallocated memory later. So yes, even though some vector may be allocated inside a called function foo, it can be released outside foo. But then again, why would you want to bother with allocating it on the heap? It's more expensive in both memory and time. Returning a vector from a function is much, much cheaper.

    As for the rest... just pick one approach. Don't try to do hybrids. They're difficult to implement and maintain. Either work exclusively with BaseNode* or use a templated type. Trying to make derived nodes or graphs keep track of the nodes or neighbors using different types will make everything more complicated. Don't use it unless you absolutely have to. Even if for future compatibility, I would just not add more complexity if I cannot justify its need. If you do realize you need it later, refactor your code then and implement it.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #19
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Don't try to do hybrids. They're difficult to implement and maintain.
    O_o

    A hybrid approach is easier to implement and maintain when correctly approached than forcing validation on the implementation or the clients to get the requested behavior.

    The problem, once again, is trying to accomplish polymorphic collections and homogeneous collections with the same code.

    The solution is exceedingly simple: use a homogeneous collection to implement the graph algorithms while still allowing the client to graph polymorphic objects as requested by moving the conditional behavior (The only conditional behavior discussed so far within the thread is one regarding the filtering of the dynamic type of particular nodes before a collection is returned to a client, but you can certainly also move other conditional behavior.) into separate code.

    The suggested approach would allow the code to hit every note requested.

    Rather than

    Code:
    template<typename Node_t>
    class XGraph
    {
        static_assert(std::is_base_of<BaseNode, Node_t>::value, "Node_t must be a type derived from BaseNode.");
        std::vector<Node_t*> m_Nodes;
        // ...
        std::vector<Node_t*> GetNeighbours(Node_t* Node);
    };
    or

    Code:
    class XGraph
    {
        std::vector<BaseNode*> m_Nodes;
        // ...
        std::vector<BaseNode*> GetNeighbours(BaseNode* Node);
    };
    you choose

    Code:
    template<typename TypeStoredWithinNode>
    class XNode
    {
        TypeStoredWithinNode m;
    };
    
    template<typename TypeStoredWithinNode>
    class XGraph
    {
        static_assert(std::is_base_of<BaseNode, Node_t>::value, "Node_t must be a type derived from BaseNode.");
        std::vector<XNode<TypeStoredWithinNode>> m_Nodes;
        // ...
        std::vector<XNode<TypeStoredWithinNode>> GetNeighbours(Node_t* Node);
        template <typename TypeStoredWithinNodeFilter> std::vector<XNode<TypeStoredWithinNode>> GetNeighbours(Node_t* Node, TypeStoredWithinNodeFilter Filter)
        {
            std::vector<XNode<TypeStoredWithinNode>> source(GetNeighbours(Node));
            std::vector<XNode<TypeStoredWithinNode>> destination;
            std::copy_if(source.begin(), source.end(), std::back_inserter(destination, Filter));
            return destination;
        }
    };
    As I have already offered, the behavior of the graphing algorithms as conditioned upon the characteristics of the types graphed do not require inheritance-based polymprhisms.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  5. #20
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    So is it still considered a hybrid when all "hybrids" are children of the same thing?
    Basically I want to be able to write base functions that can be applied to a node and allow others to be able to expand on that, but not mess with my base stuff. I dont want them to have to add to it if they dont want, but it seems like they should inherit from my base even if they dont add anything. Then I want to be able to store everything in the vector. So lets assume there is definately only one type of node used, it will be a derived node - so the BaseGraph cannot know what it is as I dont know what they will call it.
    So perhaps I should use templating...

  6. #21
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    So is it still considered a hybrid when all "hybrids" are children of the same thing?
    Basically I want to be able to write base functions that can be applied to a node and allow others to be able to expand on that, but not mess with my base stuff. I dont want them to have to add to it if they dont want, but it seems like they should inherit from my base even if they dont add anything. Then I want to be able to store everything in the vector. So lets assume there is definately only one type of node used, it will be a derived node - so the BaseGraph cannot know what it is as I dont know what they will call it.
    So perhaps I should use templating...

  7. #22
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    What I mean by hybrid is that you're trying to either:

    a) Use polymorphism and try to cast the returned by GetNeighbours to their real derived type.
    b) Use templates, but try to ensure that the graph can contain multiple types of nodes, or types of the contents of the node, not just the type you specified to graph.

    Both templated approach and pure OOP approach allows users to simply use your base graph and base nodes and allows for extending them while only adding their own functionality (the base handles the rest).
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #23
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    Hi guys,
    I have spent some time looking into templating classes, it was proving a little tricky and on my digital travels looking for help, I discovered this solution, which seemed to work. Could you please comment on this solution:

    Code:
    #include <iostream>
    #include <vector>
    
    class Base {
      public:
        Base() { }
        virtual void test() { std::cout << "I am just the base class\n"; }
        virtual Base* clone() const = 0;
    };
    
    class First : public Base {
      public:
        First() { }
        void test() { std::cout << "This is the First class\n"; }
        First* clone() const { return new First(*this); }
    };
    
    class Second : public Base {
      public:
        Second() { }
        void test() { std::cout << "This is the Second class\n"; }
        Second* clone() const { return new Second(*this); }
    };
    
    int main() {
      First *f = new First();
      Second *s = new Second();
    
      std::vector<Base *> bak;
      bak.push_back(f->clone());
      bak.push_back(s->clone());
    
      bak[0]->test();
      bak[1]->test();
    
    
    }
    I.e Is there problems with this "clone" method, I think I need to delete the initial instantiation - i.e delete f and s once I have cloned it and added it to the vector????

  9. #24
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    There are no templates in this code. What do want to achieve with this?

    Also:
    - Base should have a virtual destructor.
    - test in First and Second should be marked as virtual for readability's sake.
    - All things you new must be deleted, so that includes the pointers returned when you clone.
    - You really should start learning how to use smart pointer.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #25
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I discovered this solution
    O_o

    That is not a solution to anything so far discussed in this thread.

    The code

    Code:
    First *f = new First();
    Second *s = new Second(); 
    std::vector<Base *> bak;
    bak.push_back(f->clone());
    bak.push_back(s->clone());
    is much the same as

    Code:
    bak.push_back(new First());
    bak.push_back(new Second());
    which you were already effectively doing.

    The problems you previously faced will still come when you try to return a collection, such as `std::vector<Second>', by filtering the contents of the `bak' collection.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  11. #26
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    OK yeah that example didnt do what I expected it to do, so sorry about that....

    Going back to your previous point:

    a) Use polymorphism and try to cast the returned by GetNeighbours to their real derived type.
    what is the cost of casting? Apart from it being annoying to have to write every time, is it slow to (dynamic) cast?

    My current situation is I can get everything to do what I want if I cast, but I dont like having to type the casting each time.... (lazy...)

    It seems like what I am trying to do should be trivial! I am not sure why I am struggling with this so much. I am sorry if this conversation has gone on longer than you thought it should.

  12. #27
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I am not sure why I am struggling with this so much.
    O_o

    You made a decision to use inheritance to allow adding properties and behavior to nodes being graphed unrelated to graphing algorithms operating on such nodes.

    You are struggling because you are apparently unwilling to change that decision.

    You will not struggle to accomplish implementing the graphing algorithms without casting if you switch to a strategy using containment to allow adding properties and behavior to the graphed objects.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  13. #28
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by a.mlw.walker View Post
    what is the cost of casting? Apart from it being annoying to have to write every time, is it slow to (dynamic) cast?
    Yes, it is slow! It depends on your compiler and computer, of course, but someone did a rough benchmark: Nerdblog.com: How slow is dynamic_cast?
    (Be aware that the article is quite old; this may have improved in recent times.)
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  14. #29
    Registered User
    Join Date
    Apr 2008
    Posts
    204
    Hey,

    Interesting....
    So should my Graph class just hold the list of BaseNodes and no algorithms...
    Then my DerivedGraph holds lists of different types of Nodes, inherited from BaseNodes

    Then a class called algorothsm or something (or indeed the methods could be within the specific Nodetypes) then it doesnt matter what is in the list.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 7
    Last Post: 11-18-2012, 11:17 AM
  2. Replies: 9
    Last Post: 06-20-2008, 02:41 AM
  3. finding derived class type of a pointer to a base class
    By LinuxCoder in forum C++ Programming
    Replies: 15
    Last Post: 04-10-2006, 11:08 AM
  4. Replies: 2
    Last Post: 04-06-2005, 07:25 AM
  5. Replies: 1
    Last Post: 12-11-2002, 10:31 PM