Thread: Polymorphism - Slicing?

  1. #1
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465

    Polymorphism - Slicing?

    I am trying to store an assortment of objects, all derived from a base type, in a vector. I had had it so that the vector was a std::vector<Base>, but realized that the type of the object was being sliced trying to copy it as a Base class. I am now trying to make it an std::vector<Base &> or std::vector<Base *> but I am having a dilema with things going out of scope, and forcing the user to use new. What I ideally want it something like this:

    Code:
    class Foo {
    public:
        virtual void foo() { std::cout << "foo" << std::endl; }
    };
    
    class Bar : public Foo {
    public:
        virtual void foo() { std::cout << "bar" << std::endl; }
    };
    
    
    Foo * test(Foo & f)
    {
        return new Foo(f);
    }
    
    
    int main()
    {
        Bar b;
        Foo * f = test(b);
        
        f->foo();
        delete f;
    }
    I would want f->foo() to print 'bar' instead of 'foo', retaining it's type information after operator new is applied, but such is not the case. Is there any way I can do this, or do I just have to force clients to use operator new when adding things to the class? Is this a reasonable precondition to force on a client?

  2. #2
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Why not simply...

    Code:
    int main()
    {
        Bar b;
        Foo * f = new Bar(b);
        
        f->foo();
        delete f;
    }
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  3. #3
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Because the real situation is more like

    Code:
    class Screen
    {
        std::vector<Thingy *> v;
    public:
        void add(Thingy & t)
        {
            v.push_back(new Thingy(t));
        }
    };
    Because I don't want clients to have to use new. So instead, I figured I could solve this with a template function. But I'm getting errors still. I make the member functiona add a template

    screen.h
    Code:
    template <typename T> void add(const T & t);
    screen.cpp
    Code:
    template <typename T> void Screen::add(const T& t)
    {	
    	v.push_back(new T(t));
    	update();
    }
    But like, problems:

    1) I get a linker error saying that I have undefined references to the add function. Do I put the definition of the function in the header since templates are like 'supermacros' that need wierd instantiating or something?
    2) Do I just hope that the user isn't stupid and passes something that is derived from Thingy? It'll definitely do wierd things if it isn't.
    Last edited by Tonto; 07-23-2006 at 04:12 PM.

  4. #4
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    You can override the new operator and stop it from accepting derived types.

    But as soon as I hear the word "template" it becomes off my scope of knowledge at this moment So I'll leave this to someone else.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  5. #5
    Devil's Advocate SlyMaelstrom's Avatar
    Join Date
    May 2004
    Location
    Out of scope
    Posts
    4,079
    You have to template the class used in the new operator, otherwise you'll just be creating a Foo.
    Code:
    #include <iostream>
    
    class Foo {
    public:
        virtual ~Foo() {}
        virtual void foo() { std::cout << "foo" << std::endl; }
    };
    
    class Bar : public Foo {
    public:
        void foo() { std::cout << "bar" << std::endl; }
    };
    
    template <class T>
    T* test(T& f)
    {
        return new T(f);
    }
    
    int main()
    {
        Bar b;
        Foo *f = test(b);
    
        f->foo();
        delete f;
    }
    
    /* Will output 'bar' */
    This will work the same with your actual implementation.
    Sent from my iPadŽ

  6. #6
    Registered User
    Join Date
    Mar 2002
    Posts
    125
    A template function will let you do what you're trying in that bit of example code you posted, but it will still mean your users have to delete whatever they create themselves.
    If you want a way around that, you'll need to build a small class around your vector that automatically deletes all the objects the vector points to whenever it's deleted itself. The easiest way to do that would probably making all members of said class public so you can still use the vector's own interface.
    Another option would be not to build a class around the vector but around the pointers inside the vector.
    Last edited by Boksha; 07-23-2006 at 04:48 PM.
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Boksha nailed it on the head. Your manager class should be a lightweight wrapper around the std::vector class.
    In fact, I would prevent people from instantiating any object that is designed to be used by a container.

    Code:
    class CObjectContainer;
    class CObject
    {
      //Give CObjectContainer specific unlimited access to this class
      friend class CObjectContainer;
    
      //Magic 3 are private
      private:
        CObject &operator=(CObject &);
        CObject();
        CObject(const CObject &)
    
      public:
        virtual ~CObject();
    };
    
    //This should be a singleton, but I won't complicate this example
    class CObjectContainer
    {
      protected:
        std::vector<CObject *> m_vObjects;
    
      public:
        CObjectContainer();
        virtual ~CObjectContainer()
        {
           //Cast to DWORD to avoid size_t cast warnings
           for (DWORD i=0;i<static_cast<DWORD>(m_vObjects.size());i++)
           {
              //Cleanup memory footprint of object in vector
              delete m_vObjects[i];
           }
    
            //Clear the vector - cleanup the vector
            m_vObjects.clear();
        }   
    
        bool Add(<object params>)
        {
           //...Create object here based on params
           //For this example just instantiate the object
           CObject *pObject=new CObject();
           if (pObject)
           {
             m_vObjects.push_back(pObject);
             return false;
           } else return true     //Failed
       }
    
        bool Remove(DWORD dwIndex)
        {
           ...
        };
    
        ...
    };
    Now you directly control object creation and destruction through the manager class. It is illegal according to C++ in my example to instantiate CObject, because it's magic 3 are private.
    Last edited by VirtualAce; 07-23-2006 at 05:28 PM.

  8. #8
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Thank you for the code samples Bubba and SlyMaelstrom, it gave me some insight towards the design of my program, but I think my situation is a little different because I think I need the user to have more control. I tried to do exactly what SlyMaelstrom suggested, but I just want my template function to be a class member function like

    Code:
    template <typename T> void Screen::add(const T& t)
    {	
    	v.push_back(new T(t));
    	update();
    }
    As I said, I got linking errors from this when it was that class member function definition in a source file, and the declaration in the header. Do I have to put that whole function in a header because templates are like 'supermacro's that aren't instantiated until the compiler is given a type to instantiate it with at compile time. Or something.

    Also, what I am doing is pretty similar to what you proposed Bubba, however I think I need the user to have more control over the 'add', and don't think I could determine what the user wanted just based on parameters they passed to it. Instead, I want to be able to add things to the vector, types that are all derived from a base class which serves the basic functionality so that it would be containing a std::vector<Base *> v; And then, I add things to the vector through I guess my templated add template <typename T> void add(T & t) { v.push_back(new T(t)); } But it confuses me, will the template be able to know whether what I'm add'ing is derived from the base? Is this the only way I can preserve the type information?

  9. #9
    Devil's Advocate SlyMaelstrom's Avatar
    Join Date
    May 2004
    Location
    Out of scope
    Posts
    4,079
    Yeah, template implementation has to be in the same file as the prototype. Unfortunately, there is no way around that which I know of.
    Sent from my iPadŽ

  10. #10
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Yeah, template implementation has to be in the same file as the prototype. Unfortunately, there is no way around that which I know of.
    C++ FAQ Lite claims Comeau supports export, but the Comeau website itself says that export is soon to come.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User Tonto's Avatar
    Join Date
    Jun 2005
    Location
    New York
    Posts
    1,465
    Well, MSVC 2005 doesn't support export, and with this trivial thing, I don't think I need to do any of the funny workarounds and will just throw the definition inside the header. Thank you

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Polymorphism - "pointers" or "references"?
    By Petike in forum C++ Programming
    Replies: 10
    Last Post: 06-04-2009, 05:06 PM
  2. Question on polymorphism
    By 6tr6tr in forum C++ Programming
    Replies: 3
    Last Post: 05-06-2008, 09:05 AM
  3. Replies: 3
    Last Post: 10-31-2005, 12:05 PM
  4. Inheritance and Polymorphism
    By bench386 in forum C++ Programming
    Replies: 2
    Last Post: 03-18-2004, 10:19 PM
  5. change sorting method using polymorphism
    By Forever82 in forum C++ Programming
    Replies: 2
    Last Post: 07-31-2003, 01:21 PM