views:

213

answers:

3

Hi all. I'm getting some nasty segmentation faults through the g++ compiler on the following code. Any ideas on why this would happen and how to fix it would be great.

#include <iostream>
using namespace std;

class Base {
public:
  Base() {}
  virtual ~Base() {};
  virtual int getNum(int) = 0;
};

class Derived: public Base {
public:
  Derived() :
    Base() {}
  ~Derived() {}

  int getNum(int num) {
    return num;
  }
};

class Foo {
public:
  Foo() {
  };
  void init() {
    Derived n;
    *baseId = n;
  }
  void otherStuff() {
    cout << "The num is" << baseId->getNum(14) << baseId->getNum(15) << baseId->getNum(16) << baseId->getNum(15) << endl;
  }
  Derived* baseId;
};

int main() {
  Foo f;
  f.init();
  f.otherStuff();
  return 0;
}
+7  A: 

Here:

void init() {
    Derived n;
    *baseId = n;
}

the pointer baseId is never initialised, resulting in undefined behaviour when you dereference it. It might be a good idea to explain what you are trying to do here. If you want to maintain a pointer to a Derived or a Base but which starts off pointing to a derived, you can say:

void init() {
    baseId = new Derived;
}

but you will then probably need a copy constructor, an assignment operator and a destructor to manage the pointer.

Also, for several reasons, writing an init() function is not normally a good idea - you are better off doing the work directly in the constructor or its initialisation list.

anon
+1 for mentionning that **all 3** usually compiler provided functions should be defined manually.
Matthieu M.
+3  A: 

When you call f.init(), the baseId member of Foo is not initialised, yet you dereference it in init(). Are you sure you don't want something more along the lines of:

baseId = new Derived()
dreamlax
Jon2029
dreamlax
@dreamlax Correct. n disappears after the function returns, so the pointer becomes invalid again.
anon
+2  A: 
  void init() {
    Derived n;
    *baseId = n;
  }

Apart from what Neil noted, derived n is local to your init function. It "dies" when you exit the function, so even if you assigned it correctly, it won't work.

What you want is not assigning on the stack but on the heap:

  void init() {
    baseId = new Derived();
  }

or even better:

  void init() {
    delete baseId;
    baseId = new Derived();
  }

and a destructor and constructor pair to prevent problems :

Foo() : baseId(0) {};
~Foo() { delete baseId; }

If going for this method, be sure to either block copy constructor and assignment operator, or implement them properly. To implement them however, you'd need to implement copying of Derived too -- or best: use a safe shared_ptr to store the pointer.

Kornel Kisielewicz
Very good. I did not understand that Derived() allocates on the stack and new allocates on the heap. thanks for the tip. (as these things tend to be, the actual code was quite a bit more complex)
Jon2029
You don't need to write `if (baseId) delete baseId;` but can simply write `delete baseId;` (deleting a null pointer is legal and is effectively a no-op).
R Samuel Klatchko
@R Samuel -- I know, note that I did take that into account in the destructor -- I wrote so for completeness sake though.
Kornel Kisielewicz
If we were to meet, I would bash you on the head. I don't mean to seem pedantic, but I've been hurt so many times by such classes... Here are the `Sacred Three`: `Copy Constructor`, `Assignment Operator` and `Destructor`. If you define any of them, define the other 2, or go to hell (with your fellow developers).
Matthieu M.
@Matthieu, I proposed this solution, but myself, I'd never allow a non-managed pointer to dangle around ;>
Kornel Kisielewicz
Unfortunately I am always afraid that the responses will be taken at face value. Not that I sponsor copy/paste coding... but I think it's better to give all clues in one place rather than scattered among the many answers :)
Matthieu M.
@Matthieu -- here, I updated the answer a little, so that you can sleep peacefully ;)
Kornel Kisielewicz