views:

722

answers:

7

I'm still learning C++; I was trying out how polymorphism works and I got a segmentation fault when calling a virtual method.

(Note: I didn't mark the destructor as virtual, I was just trying out to see what happens.) Here's the code:

#include <iostream>

using namespace std;

class Base
{
protected:
  char *name;

public:
  Base(char *name)
  {
    cout << name << ": Base class cons" << endl;
  }

  ~Base()
  {
    cout << name << ": Base class des" << endl;
  }

  virtual void disp();
};

void Base::disp()
{
  cout << name << ": Base disp()" << endl;
}

class Child : public Base
{
public:
  Child(char *name):
    Base(name)
  {
    cout << name << ": Child class cons" << endl;
  }

  ~Child()
  {
    cout << name << ": Child class des" << endl;
  }

  virtual void disp()
  {
    cout << name << ": Child disp()" << endl;
  }
};


int main()
{
  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();
}

Also, if you've any other tips regarding the usage of inheritance and polymorphism in general for someone who knows these concepts in Java, please let me know. Thank you!

+1  A: 

I don't think you're assigning the member char * name to anything in your ctors.

drewh
+1  A: 

Whoa there.

There's a few problems, but your segfault is probably because you're passing a char* -- which is just a pointer, and then trying to cout it in disp(). Problem is, that pointer does not live in disp(), it lives in main(). You probably want to either deep-copy the char*, or use std::string. Doing it this way will not work.

EDIT:

See EDIT 2

You can not just assign name to the class's name variable. If you do that, you'll get unpredictable results - and you'll probably STILL segfault. Remember: in C/C++, objects are locally scoped unless allocated on the heap. In this case, in your ctor, you'd want to do something like:

this->name = new char[ strlen( name ) + 1 ];
strcpy( this->name, name );

And in the destructor, you'll want to do something like:

delete [] this->name;

Note: my syntax may be completely wrong, and I realize the above code is inherently unsafe as you're not checking the char* to make sure it's not NULL, and you're not checking the return value of new. Nevertheless, this should get you started.

EDIT 2: I stand corrected. String literals are treated as constant storage and thus live on for the duration of the program. Nevertheless, the lesson, I believe, is important: in general, when not dealing with string literals, passing a pointer (or array, etc.), you need to allocate storage for it and deep-copy. You also need to de-allocate appropriately when destroying said object.

FreeMemory
That's not a problem here (and still wouldn't be, if the constructors actually stored the parameter). String literals have static storage duration. Of course it would be a problem is someone created a Child from a string with dynamic or automatic storage duration, and the Child outlived the string.
Steve Jessop
@FreeMemory - just about everything you say here is wrong. It is perfectly OK to hold a pointer to a string literal - no need to copy it.
anon
Specifically, see 2.13.4/1 and 3.7.1/1 in the Standard.
Steve Jessop
@Neil Butterworth, @onebyone: I stand corrected. You are correct in reference to string literals.
FreeMemory
@FreeMemory: even after edit2, there is nothing wrong with storing a pointer into a different object and is even required in many cases. That is, ass long as you do know that the pointed object outlives the timespan of the pointer.
David Rodríguez - dribeas
@dribeas: Of course you're right - hence why I included the words "in general." Of course there are times when you can just pass a pointer and not deep-copy. Globals are a good example. But a classic "beginner" C++ mistake is to pass a pointer when you meant to deep-copy. Perhaps I should clarify.
FreeMemory
+6  A: 

name - is unintialized in Base

also you have not one problem:

  Base c = Child("2");

I think it not that what you want. Your code will create instance of Base from casted Child. But I think you want work with Child instance due Base interface, you should write next:

  Base *c = new Child("2");

also, for avoid next bugs - declare destructor in base as virtual.

bb
So, if I need polymorphic behavior, then do I have to store my object in the heap?
artknish
Not really. You can something like this. Child a("2"); Base *ptr =
chappar
No. You could create object on stack, after that pass it to some function as parameter by reference on Base.. and you will have polimorphic behavior. Or as in @chappar answer (in previous comment).
bb
Thanks! My mind just stopped working for some reason...
artknish
Don't stop thinking :-) It's just because storage place of objects is always the same in Java and .NET while in C++ you can decide to store it on heap or stack. (it is also possible to put objects in other memory locations [shared mem etc.] but that's for the freaks :-) )
rstevens
@Srikanth: ask if you have any troubles.
bb
+4  A: 

You never initialise the base nenber variable - your base constructor should be:

Base(char * aname) : name( aname )
  {
    cout << name << ": Base class cons" << endl;
  }

As well as that, when you say

Base b = Child( "xxx" );

then the Child instance will be sliced down to a Base, which is probably not what you want.

anon
+1  A: 

The Child::disp() method will never be called - c is a variable of type Base, and not a pointer or reference, so it won't check for virtual methods.

Base * c = new Child("1");
c->disp();
delete c;

would call Child::disp().

Khoth
A: 

There are few problems here. The first thing is your base class destructor has to be virtual. Otherwise your base class destructor will always be called even if it points to derived object. Second is you should not assign derived class object to base class object. This is called object slicing. So, assignment should be done through pointer or reference.

The segmentation problem is happening because it contains garbage value. You need to initialize it in the constructor.

chappar
A: 

You have a couple of problems with your code.

First, and the reason why your getting a segfault, is the implementation of the Base ctor takes a parameter of the same name as one of the class' member variables:

class Base
{
protected:
  char *name;

public:
  Base(char ***name**)
  {
    cout << name << ": Base class cons" << endl;
  }

The ctor's parameter 'name' hides the class' member variable of the same, erm... name.

Second, you are slicing your object here:

int main()
{
  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();
}

'c' is of type Base, and you are trying to assign a Child to it. All of the stuff that is unique to Child will be sliced off when you assign the ogbject to the base class.

Here is code that fixes both these problems:

#include <iostream>
#include <string>

using namespace std;

class Base
{
protected:
    std::string name_;

public:
  Base(char *name)
      : name_(name) {
    cout << name_ << ": Base class cons" << endl;
  }

  ~Base()
  {
    cout << name_ << ": Base class des" << endl;
  }

  virtual void disp();
};

void Base::disp()
{
  cout << name_ << ": Base disp()" << endl;
}

class Child : public Base
{
public:
  Child(char *name):
    Base(name)
  {
    cout << name_ << ": Child class cons" << endl;
  }

  ~Child()
  {
    cout << name_ << ": Child class des" << endl;
  }

  virtual void disp()
  {
    cout << name_ << ": Child disp()" << endl;
  }
};


int main()
{
  //Base b;
  //b.disp();
  Base * c = new Child("2");
  c->disp();
  delete c;
}
John Dibling