Advertisement Jump to content
Sign in to follow this  
george7378

Inheritance question

This topic is 1791 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi!

 

I'm writing a program with a base class and multiple derived classes which are stored in a vector. I think it's easiest if I just post a barebones version to demonstrate my problem:

#include <iostream>

#include <vector>



using namespace std;



class Base

{

public:



    Base(){};



    virtual void identify() const

    {

        cout << "Base" << endl;

    }

};



class Derived1 : public Base

{

public:



    Derived1(){};



    void identify() const

    {

        cout << "Derived1" << endl;

    }

};



class Derived2 : public Base

{

public:



    Derived2(){};



    void identify() const

    {

        cout << "Derived2" << endl;

    }

};



int main()

{

    //Static

    vector <Base> vec;

    vec.push_back(Derived1());

    vec.push_back(Derived2());



    vec[0].identify();

    vec[1].identify();



    //Dynamic

    vector <Base *> vecdyn;

    vecdyn.push_back(new Derived1());

    vecdyn.push_back(new Derived2());



    vecdyn[0]->identify();

    vecdyn[1]->identify();



    while (!vecdyn.empty())

    {

        Base *element = vecdyn.back();

        vecdyn.pop_back();

        delete element;

    }



    cin.get();

    return 0;

}

Running the above program gives me this result:

 

Base

Base

Derived1

Derived2

 

...so the Derived1 and Derived2 objects stored in the static vector identify themselves as Base objects while the ones stored in the dynamic vector identify themselves as their respective derive classes. Why are they doing this, and is there a way to make the static objects identify themselves as derived classes rather than base ones?

 

Much appreciated smile.png

Edited by george7378

Share this post


Link to post
Share on other sites
Advertisement

What BitMaster said. I would use std::vector<std::unique_ptr<Base>>.

 

Also, your base classes should always have a virtual destructor.

Share this post


Link to post
Share on other sites

A longer description of the problems.

 

When you have a base object rather than a base pointer, you are telling the compiler to make certain assumptions. When you use the type directly the language assumes you are not invoking certain behaviors, that the object really is the type you specified and not a derived type.

 

Also note that you don't have a virtual destructor.  A destructor should either be public and virtual (meaning you can use it as a base class) or protected and non-virtual (meaning you cannot inherit from it.) This has the same basic problem, if you don't do the destructor properly the object can be incompletely destroyed.

Share this post


Link to post
Share on other sites


Also note that you don't have a virtual destructor.  A destructor should either be public and virtual (meaning you can use it as a base class) or protected and non-virtual (meaning you cannot inherit from it.) This has the same basic problem, if you don't do the destructor properly the object can be incompletely destroyed.

 

I know this is the correct thing to do, and I am in no way advocating NOT making it virtual, but in this case does it actually matter in practice?

i.e. Derived has no extra memory overhead, so the allocated space for Base is released? Just a question.

Share this post


Link to post
Share on other sites

Also note that you don't have a virtual destructor.  A destructor should either be public and virtual (meaning you can use it as a base class) or protected and non-virtual (meaning you cannot inherit from it.) This has the same basic problem, if you don't do the destructor properly the object can be incompletely destroyed.

 
I know this is the correct thing to do, and I am in no way advocating NOT making it virtual, but in this case does it actually matter in practice?
i.e. Derived has no extra memory overhead, so the allocated space for Base is released? Just a question.


The fact that Derived has nothing extra to do in the destructor is a detail that Base shouldn't be aware of. In particular, if that changes in the future, only the code in Derived should have to change.

Share this post


Link to post
Share on other sites

I know this is the correct thing to do, and I am in no way advocating NOT making it virtual, but in this case does it actually matter in practice?
i.e. Derived has no extra memory overhead, so the allocated space for Base is released? Just a question.


The fact that Derived has nothing extra to do in the destructor is a detail that Base shouldn't be aware of. In particular, if that changes in the future, only the code in Derived should have to change.


Yep, that's why I said making it virtual was the Right Thing To Do(tm).

My question was whether, in this particular instance, a non-virtual destructor would break the code, i.e. is the behaviour undefined?
It was an academic question.

Share this post


Link to post
Share on other sites
I believe destructing a Derived through a pointer to Base is indeed undefined behavior if Base doesn't have a virtual destructor.

To make matters less clear smile.png , this does work:
#include <iostream>
#include <memory>

struct Base {
  Base() {
    std::cout << "Base\n";
  }

  ~Base() {
    std::cout << "~Base\n";
  }
};

struct Derived : Base {
  Derived() {
    std::cout << "Derived\n";
  }

  ~Derived() {
    std::cout << "~Derived\n";
  }
};

int main() {
  std::shared_ptr<Base> p(new Derived);
  // The constructor of std::shared_ptr<Base> is a template and it will remember to call the correct destructor for the type passed!
}
Edited by Álvaro

Share this post


Link to post
Share on other sites
If the incorrect function is never called, yes, it is well-defined.

However, rather than just hoping it is correct and that nobody ever misuses it, or through documentation and verification and luck never doing an incorrect function call, it is just so much easier to follow the well-established convention and remove all doubt.

Share this post


Link to post
Share on other sites
Thanks for the responses, so am I right in thinking that a virtual destructor should be something like this in the base class:

virtual ~Base(){};
...although I shouldnt need a destructor unless I allocate dynamic memory within the class, right?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!