Thread: Problem with Polymorphism

  1. #1
    Registered User
    Join Date
    Mar 2006
    Location
    Farther than you think
    Posts
    11

    Problem with Polymorphism

    Alright, here's the problem.

    I need to make a copy of an instance of a derived class by only referring to its base class. To illustrate this problem, let me provide some code.

    Code:
    //Base ordnance class - to be inherited by other ordnance objects
    class Ordnance{
    
        //Virtual methods go here
        public:
    
            virtual void initialize(float fXPos, float fYPos, float fAz){
                std::cout << "Program shouldn't be getting here!" << std::endl;
            }//endfunction
    
            ....
    
    };//class
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    //Bolt class - a weapon type that extends the Ordnance class
    class Bolt : public Ordnance{
    
        //Overriding functions
        public:
    
            void initialize(float fXPos, float fYPos, float fAz){
                std::cout << "This is where the program SHOULD go!" << std::endl;
            }//endfunction
    
        ...
    
    };//class
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    //Weapon type installed into Turret
    class Weapon{
    ...
    
        public:
    
            //Constructor
            Weapon(Ordnance ordnance){
                this -> ordnance = ordnance;
            }//endconstructor
    
            void fire(float fXPos, float fYPos, float fAz){
    
                //Create a copy of the ordnance using the automatic copy constructor
                //and add it to the universal object system.
                Ordnance *newOrd = new Ordnance(ordnance);
                newOrd -> initialize(fXPos, fYPos, fAz);
                objects.registerNewObject(newOrd);
    
                //The problem is above; I think that declaring the derived class - which
                //should be of type Bolt - as an Ordnance type somehow casts it...how
                //can I work around this?
    
        private:
    
            //Copy of ordnance type
            Ordnance ordnance;
    
    };//class
    
    ...
    
    Bolt plasmaBolt;
    Weapon weap(plasmaBolt);
    weap.fire(0.0f, 0.0f, 0.0f);
    The output gives:


    "Program shouldn't be getting here!"


    Instead of,


    "This is where the program SHOULD go!"


    How can I prevent the Bolt from being turned into an Ordnance type? (If this is indeed the problem) Thanks!

    - Aramil
    Last edited by Aramil; 04-15-2008 at 07:22 PM. Reason: (Forgot to declare the Bolt as a derived class of Ordnance)

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Well, but you're not calling the initialize method of Bolt, you're calling the initialize method of "new Ordnance", which is, as noted, an Ordnance. You probably need to store a pointer to what is initialized, rather than create a new Ordnance object, which can never inherit the Bolt-ness of the Bolt.

  3. #3
    Registered User
    Join Date
    Mar 2006
    Location
    Farther than you think
    Posts
    11
    Okay, so I was right.

    Could you please explain a little, perhaps using my code to outline your point? (ie. what would you write, if you were using that code)

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    In the constructor, instead of taking an object, take a pointer to an object and store that pointer somewhere as an Ordnance *. Then later you can call ->initialize from that * (rather than creating a new one from scratch that doesn't have anything in it).

  5. #5
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by Aramil View Post
    Okay, so I was right.

    Could you please explain a little, perhaps using my code to outline your point? (ie. what would you write, if you were using that code)
    If you create an "abstract data type" (a search friendly term) you could eliminate the possibility of creating Ordinance objects at all. If that is indeed what you intended, then it seems ideal.

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Indeed, instead of:
    Code:
    virtual void initialize(float fXPos, float fYPos, float fAz) {
        std::cout << "Program shouldn't be getting here!" << std::endl;
    }
    You should just write:
    Code:
    virtual void initialize(float fXPos, float fYPos, float fAz) = 0;
    Of course, you would then be unable to store an Ordnance member, but doing that was wrong to begin with since it would result in type slicing, where despite passing an object of a derived class type, the derived class aspects of the object is sliced away and only what is of the base class remains. It is this type slicing behaviour that caused the Ordnance initialize member function to be called instead of the Bolt initialize member function.

    The solution to this is what tabstop pointed out, i.e., store a (smart) pointer to Ordnance, so type slicing will not occur and polymorphism would work as expected.
    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

  7. #7
    Registered User
    Join Date
    Mar 2006
    Location
    Farther than you think
    Posts
    11
    Okay; I've implemented tabstop's idea. The code looks like this:

    Code:
    //Base ordnance class - to be inherited by other ordnance objects
    class Ordnance{
    
        //Virtual methods go here
        public:
    
            virtual void initialize(float fXPos, float fYPos, float fAz){
                std::cout << "Program shouldn't be getting here!" << std::endl;
            }//endfunction
    
            ....
    
    };//class
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    //Bolt class - a weapon type that extends the Ordnance class
    class Bolt : public Ordnance{
    
        //Overriding functions
        public:
    
            void initialize(float fXPos, float fYPos, float fAz){
                std::cout << "This is where the program SHOULD go!" << std::endl;
            }//endfunction
    
        ...
    
    };//class
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
    //Weapon type installed into Turret
    class Weapon{
    ...
    
        public:
    
            //Constructor
            Weapon(Ordnance *ordnance){
                this -> ordnance = ordnance;
            }//endconstructor
    
            void fire(float fXPos, float fYPos, float fAz){
    
                //Create a copy of the ordnance using the automatic copy constructor
                //and add it to the universal object system.
                ordnance -> initialize(fXPos, fYPos, fAz);
                objects.registerNewObject(ordnance);
    
                //The problem is above; I think that declaring the derived class - which
                //should be of type Bolt - as an Ordnance type somehow casts it...how
                //can I work around this?
    
        private:
    
            //Copy of ordnance type
            Ordnance *ordnance;
    
    };//class
    
    ...
    
    Bolt *plasmaBolt = new Bolt;      //This
    Weapon weap(plasmaBolt);       // and this will only be called once
    weap.fire(0.0f, 0.0f, 0.0f);          //This will be called multiple times per run
    This does work (and I appreciate the help a lot), but unfortunately, this leads to a problem. The Weapon::fire() method will obviously be called more than once while the program runs, which means that its ordnance will be added to the object system as many times as this method has been called. Because it is repeatedly adding the same Ordnance variable to the object system (and all of those elements in the linked list of the system refer to the same variable, not a new copy), the result is that that particular variable (ordnance) is updated multiple times (the number of times the fire() method is called) per program step. In other words, I need to create a new copy of the Ordnance variable.

    Any ideas? Thanks in advance.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    From what I see, you want to write something along these lines:
    Code:
    // Abstract base ordnance class
    class Ordnance {
    public:
        virtual void initialize(float fXPos, float fYPos, float fAz) = 0;
        virtual ~Ordnance() {}
        virtual Ordnance* clone() const = 0;
    };
    
    //Bolt class - a weapon type
    class Bolt : public Ordnance {
    public:
        //Overriding functions
        void initialize(float fXPos, float fYPos, float fAz) {
            std::cout << "This is where the program SHOULD go!" << std::endl;
        }
    
        Bolt* clone() const {
            return new Bolt;
        }
    };
    
    //Weapon type installed into Turret
    class Weapon{
    public:
        Weapon(const Ordnance* ordnance) : ordnance(ordnance) {}
    
        void fire(float fXPos, float fYPos, float fAz) {
            Ordnance* current_ordnance = ordnance->clone();
            current_ordnance->initialize(fXPos, fYPos, fAz);
            objects.registerNewObject(current_ordnance);
        }
    private:
        Ordnance* ordnance; // Associated ordnance.
    };
    Note that you should delete current_ordnance or wrap it in a std::auto_ptr if the objects subsystem does not delete it. Alternatively, consider using std::tr1::shared_ptr and thus avoid explicitly having to delete it in both fire() and from the objects subsystem.

    Now, from what I see, the Weapon class does not have ownership, in terms of composition, of its Ordnance. Therefore, you can write:
    Code:
    Bolt plasmaBolt;
    Weapon weap(&plasmaBolt);
    weap.fire(0.0f, 0.0f, 0.0f);
    In case you were not aware, Bolt::clone() overrides Ordnance::clone() even though the return types are different, due to the concept of covariant return types: since a Bolt is-a Ordnance by public inheritance, the overriding clone() virtual function may return a Bolt* instead of an Ordnance*.
    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

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Note that I would consider it a good idea to explicitly add virtual to all the virtual functions, even if the base class's function is virtual. It's not required that the derived function also be explcitly virtual if the base class's is, but it may avoid confusion.
    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. #10
    Registered User
    Join Date
    Mar 2006
    Location
    Farther than you think
    Posts
    11
    Thanks, laserlight; your solution worked perfectly. The only changes I made were to the copy function in the Bolt class:

    Code:
    Bolt* clone(){
        return new Bolt(*this);
    }//endfunction
    As you can see, it returns a pointer copy of itself. This was necessary because the Bolt class obviously had other members (damage, speed, size, etc.) that need to be copied.

    Thanks again!

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    As you can see, it returns a pointer copy of itself. This was necessary because the Bolt class obviously had other members (damage, speed, size, etc.) that need to be copied.
    Yes, that is a correct fix.
    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

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Someone having same problem with Code Block?
    By ofayto in forum C++ Programming
    Replies: 1
    Last Post: 07-12-2007, 08:38 AM
  2. A question related to strcmp
    By meili100 in forum C++ Programming
    Replies: 6
    Last Post: 07-07-2007, 02:51 PM
  3. Polymorphism Problem
    By ltanusaputra in forum C++ Programming
    Replies: 3
    Last Post: 04-25-2007, 11:36 AM
  4. WS_POPUP, continuation of old problem
    By blurrymadness in forum Windows Programming
    Replies: 1
    Last Post: 04-20-2007, 06:54 PM
  5. Polymorphism - Can't Articulate Problem
    By Tonto in forum C++ Programming
    Replies: 17
    Last Post: 11-13-2006, 05:24 PM