views:

207

answers:

7

I have just created 2 pointers which has undefined behavior and try to invoke a class member function which has no object created ?

I don't understand this?

#include<iostream>

using namespace std;

class Animal
{
public:
  void talk()
  {
    cout<<"I am an animal"<<endl; 
  }
};

class Dog : public Animal
{  
public:
  void talk()
  {
    cout<<"bark"<<endl; 
  }
};

int main()
{
  Animal * a;
  Dog * d;

  d->talk();
  a->talk();  
} 
A: 

You need to use the new operator.

JRL
And mark `Animal::talk` as `virtual`
Travis Gockel
@Travis G: Actually I believe it is legal to hide an inherited name this way in C++ -- though admittedly it's bad practice and is not going to give someone used to Java what they expect ;)
Billy ONeal
@BillyONeal: You are right; using name hiding will not make the program run ridiculously slow. :-D (I kid! I kid! Sort of...)
James McNellis
@James McNellis: Kidding is perfectly fine so long as you understand there will be no Unicorn eating going on :P
Billy ONeal
Sorry for the terse question. When i ran this program expecting it to not work, it surprisingly works!!! I understand what I am trying to do is an undefined behavior but could anyone explain under what circumstances of "the undefined" behavior does this work ?
Eternal Learner
+9  A: 

When you do something that has undefined behavior, anything can happen -- including it appearing to work. It looks like that's what's happening to you in this case.

Jerry Coffin
+1 for answering what appears to be the actual question.
Carl Norum
+1 to above comment and to answer. Of course, we cannot be sure that what appears is what is :P
Billy ONeal
Exactly. It is working in my compiler but I do not understand under what circumstances it can work?
Eternal Learner
@Srinivasa: Under **no** circumstances does undefined behavior work. **None**. Under _any_ circumstances it may _appear_ to work, but that's it.
James McNellis
@Srinivasa, you will have to look at a disassembly of the binary for your program to get more information about that. Maybe looking at the source code for the compiler will help you if that is available. My best guess is that this appears to work because the methods in question don't depend in any way on details about the particular instance of the object. The compiler might be changing those calls to simple function calls, and ignoring the fact that they're being used on uninitialized pointers.
Carl Norum
+1. While my answer goes into detail as to why the specific undefined behavior is observed, your answer (it's undefined, don't do it) is the *definitive* answer.
kyoryu
A: 

You need to change:

void talk()

To:

virtual void talk()

If you want the function to be polymorphic. Also, you need to instantiate your objects as in:

Animal* a = new Animal;
Dog* d = new Dog;

Don't forget to free them before you return:

delete a;
a = 0; 

delete d;
d = 0;

In practice, you will want to use a boost::shared_ptr or std::auto_ptr as in:

std::auto_ptr<Animal> a(new Animal);
std::auto_ptr<Dog> b(new Dog);

The above will save you the need to call delete. Alternatively, you can use automatic storage:

Animal a;
Dog d;

With the above, you would use a.talk() and d.talk() instead of a->talk() and d->talk().

Michael Aaron Safyan
Fixed your mispelling of delete. Side note though -- you really should be using NULL rather than 0 to set pointers to NULL (or `nullptr` if you're lucky enough to have a compiler that supports that)
Billy ONeal
`auto_ptr`? Yuck. At least `scoped_ptr` or `shared_ptr` please :)
Billy ONeal
@BillyONeal, I disagree... with NULL you have to include <cstddef>... using 0 doesn't require any includes. In C++0x, though nullptr would be ideal.
Michael Aaron Safyan
@Billy, I agree on auto_ptr... but until std::shared_ptr and std::scoped_ptr is standard, I will refer to Boost (boost::shared_ptr and boost::scoped_ptr) and to std::auto_ptr (for those who want to stick with just the standard library).
Michael Aaron Safyan
@Billy: I'm curious about your rational for that.
GMan
I should clarify, I mean NULL over 0. I think it's complete preference, but the way you worded it makes it sound like NULL has something which 0 does not. Maybe I'm reading too much into it.
GMan
@GMan: Two reasons. The first is that semantically, NULL is the NULL pointer constant. Second, because it makes it easier to update your code to use `nullptr` when you do eventually decide to do that, because it should be a simple find and replace operation at that point.
Billy ONeal
A: 

I believe the reason your code works is because the talk() methods are not actually accessing any member variables of the class. In other words, you are not actually accessing the implicit this, which happens to be invalid.

I actually experienced this same issue before. My code was calling a member function of a null pointer and it reliably worked. I finally discovered the problem when I modified the function so that it actually attempted to access a member variable. After that change it reliably crashed.

I'm not sure if this is standard behavior or compiler specific. In my case I was using Microsoft Visual Studio 2008.

flashk
It is not standard behaviour, it is undefined behaviour. If your compiler chooses to make it appear to work, that's up to its implementors.
Carl Norum
A: 

It's undefined behavior, so anything might happen.

It can be possible that it just prints the correct thing since the methods don't access any member variables of the objects they are called on (the memory where the objects supposedly live doesn't need to be accessed, so access violations don't necessarily occur).

Unless your compiler specifies this kind of behavior somewhere (which it most probably won't), you can of course not count on this to happen.

sth
+10  A: 

A) It's undefined behavior. Any behavior may happen.

B) Since you're not calling a virtual method, it's pretty easy to explain why the undefined behavior actually does this (and I've tested this under just about every compiler I could find).

In C++, calling a member method is equivalent (in practice if not in definition) of calling a member with a hidden 'this' variable. If the method is virtual, it has to go through the vftable, but not for a non-virtual method.

So

Foo::Bar(){}

is the rough equivalent of

Foo_Bar(Foo *this){}

and in the calling code

Foo *foo = new Foo();
foo->bar();

the second line is roughly the moral equivalent of

Foo_Bar(foo);

Now, there's some simplification going on here, and as I said, some of this may be implementation detail rather than specification. However, the behavior holds true (though relying upon it is an error).

But, given the preceding, look at an implementation:

void Foo::Bar(){printf("Hello, world!\n");}

and calling code:

Foo *foo = 0;
foo->Bar();

As we've said, this is roughly equivalent (since we're non-virtual) to:

Foo *foo = 0;
Foo::Bar(foo);

Which means that we're calling the equivalent of the following method:

void Foo_Bar(Foo* this)
{ printf("Hello, world\n"); }

Now, given this method, we're not actually dereffing the this pointer! So, it's pretty clear why, in this case, the method will work and not fail.

The practical upshot of this is that calling non-virtual methods on a null pointer, where the method doesn't deref an a member, will generally result in this observed behavior. But, relying upon any undefined behavior is, basically, evil.

kyoryu
A: 

you call this non-static member method, you should construct the object of class, so you need:

 Animal * a =  new Animal();
 Dog * d = new Animal();

 d->talk();
 a->talk(); 

 delete a;
 delete d;

And to use Polymorphism, you should use virtual keyword before talk()

public:
virtual void talk()
  {
    cout<<"I am an animal"<<endl; 
  }
Mark Yin