3

I thought for a long time that to use virtual functions, the class object has to be allocated with a pointer but I ran a test example and it seems it will perfectly work for objects on stack, it only depends on the syntax how you call it. But what I am doing feels almost like I am creating a 'cheat'to making the system do what I want, that is use a plain base class pointer to call a derived class virtual method.

#include <iostream>

using namespace std;

class B{
public:
    virtual void call() { cout<<"B\n"; };
};

class D: public B{
public:
    void call()  {  cout<<"D\n"; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    B b;
    D d;

    b.call(); // output is "B"

    d.call(); // output is "D"

    B* obj = &d;
    obj->call(); // output is "D"

    return 0;
}

Is this acceptable to do in release software or will it raise eyebrows? I can use this to great advantage in my software but I haven't seen anything like this before.

In my actual project, I have base and derived classes which are allocated on stack, lets call them Base, DerivedA and DerivedB. I want to restructure and add virtual functions to the base and I thought I will have to allocate them on heap now but looks like I don't have to. Now my application could be in certain state where all operations will be done on DerivedA or DerivedBso I plan to add a pointer object:

Base * obj = &DerivedA;

Obviously when I need switch operations to other object, I just do obj = &DerivedB and basically I will be using both objects indirectly through my new objpointer for all operations. Does this look sound?

Currently my code has if else statements to do operation either on DerivedA or DerivedBand this will eliminate those.

zar
  • 11,361
  • 14
  • 96
  • 178
  • Everyone who's ever created an `fstream` on the stack has done this exact same thing. – Mooing Duck Mar 05 '15 at 18:39
  • Q: I thought for a long time that to use used virtual functions, the class object has to be allocated with a pointer. A: Nothing could be further from the truth. Yes, virtual functions happens to use "pointers", which happen to be in a "vtable". But that's an "implementation detail". Your class doesn't know or care whether or not there's a "vtable". And it doesn't matter whether your class object is stack or heap for it to have a vtable. – FoggyDay Mar 05 '15 at 18:41
  • 1
    @FoggyDay I dunno, I think there are some things quite a bit further from the truth. This misconception is at least understandable: virtual functions are typically explained (in classes, books, etc) as a way of handling *pointer* polymorphism. – Kyle Strand Mar 05 '15 at 19:03
  • @KyleStrand: The badness here is mixing up pointers and lifetime, when in reality they have little to do with each other- you can point to an object with any lifetime. – Puppy Mar 05 '15 at 20:12
  • @Puppy Oh, certainly--hence dangling pointers. See the last paragraph of my answer. – Kyle Strand Mar 05 '15 at 20:41

2 Answers2

10

It's perfectly normal. One of the golden rules of C++ is that objects should not care who owns them - including the stack. It's not common to hold a base pointer to a derived object in the same stack frame, as that would raise questions as to why you're not just using it directly, but it's perfectly legal and common to reference automatic-storage-duration objects polymorphically.

Notice that I said "automatic storage duration" and not "on the stack". That's because the same rules apply to any automatic storage duration objects- class members being the most obvious example.

user229044
  • 232,980
  • 40
  • 330
  • 338
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I am using qml and this stuff is in the c++ model code. I am exposing the two objects to qml but the operations are `if else` but now I want to expose a pointer as well and set it in qml to the correct object which will be used for all operations. This is why I am creating the pointer even though it is in the same stack but effectively it lives through out application. – zar Mar 05 '15 at 18:47
  • @zadane Why not just put the `if else` stuff in another function, which would return a reference of the base-class type? – Kyle Strand Mar 05 '15 at 18:59
  • @KyleStrand I think setting at one pointer should be cleaner, it goes somewhat along state design pattern. – zar Mar 05 '15 at 19:06
  • @zadane As opposed to setting one reference? What's the difference? – Kyle Strand Mar 05 '15 at 19:35
  • References cannot be reseated. Also, returning it from a function would imply dynamic management of the lifetime instead of automatic. – Puppy Mar 05 '15 at 19:40
  • @KyleStrand sorry that was typo, i meant 'setting at one *point* should be clearer'. I am not sure what you mean by references. – zar Mar 05 '15 at 19:47
  • @zadane Ah. Well, you'd still be setting it at one point; you'd just be using a reference instead of a pointer. I'll add some code to my answer so you can see. – Kyle Strand Mar 05 '15 at 21:30
  • @zadane Sorry, had some distractions. I've added an example. – Kyle Strand Mar 05 '15 at 23:41
  • @Puppy It's not clear to me that the variable *should* be reseatable in this case. I'm not sure what you mean by dynamic vs lifetime management of the variable; is my example (at the bottom of my answer) equivalent to what you're imagining the inline `if` statement would do? – Kyle Strand Mar 06 '15 at 19:13
5

There is no distinction between pointers to objects in the heap, pointers to objects on the stack, or pointers anywhere else in memory.

There is no (internal) distinction between objects on the stack or objects on the heap: they have the same members, including the v-table pointer, regardless.

Thus, what you're doing is perfectly acceptable. You can see a more common version of this idiom when a function that takes a pointer as an parameter is called with a local object as an argument:

MyObj foo;
MyFunc(&foo);

The "C" way of thinking about this call, which is that the actual argument given is the address of foo, gives some insight here: this is a standard way of passing a pointer to a stack-local object to a function. Within the body of that function, of course, there's no way of knowing whether the object is on the stack, on the heap, or elsewhere; thus, we expect virtual functions to be treated the same way regardless.

There is, of course, a caveat, which is you should never declare an object on the stack as a pointer (e.g. MyObj* foo = &foo{};). See here for an old answer of mine in which I completely biffed this (though I later edited the question to make it clear to new viewers that my example is bad practice even if it can work in some cases with some compilers; look at the edit history to see the original version of the answer, which earned me some well-deserved downvotes).


EDIT: Instead of using a pointer directly, you could consider using a reference. Below is a complete example program that demonstrates this:

#include <iostream>

using namespace std;

class Base
{
  public:
    virtual const char* func()
    {
      return "Base::foo\n";
    }
};

class Derived: public Base
{
  public:
    virtual const char* func()
    {
      return "Derived::foo\n";
    }
};

inline Base& pickObj (int i, Base& obj1, Base& obj2)
{
  if (i > 0)
  {
    return obj1;
  }
  else
  {
    return obj2;
  }
}

int main()
{
  Base base;
  Derived derived;
  Base& my_obj{pickObj(0, base, derived)};
  cout << my_obj.func();
}

When run, the output is:

Derived::foo
Community
  • 1
  • 1
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • Thanks your answer was helpful but I am not sure if I will agree with the example. The pickObj in my case needs to decide on external elements and I think it will be better to set the state until you change it rather than test the if condition every time you need an object even though it is inside this function now. – zar Mar 06 '15 at 19:12
  • @zadane I'm not sure what you mean by "external elements"; my `int i` is intended to represent some arbitrary set of conditions that of course you'd need to tailor to your needs. Of course you know best what implementation would best fit your specific needs; I just wanted to clarify exactly how using a reference would work, as well as how hiding the selection logic in a function would work. – Kyle Strand Mar 06 '15 at 19:15
  • I got your point with reference but I ran it and actually always calls the base class with the reference! So the reference does't really work! – zar Mar 06 '15 at 19:28
  • @zadane Er....as stated, the output when run is `Derived::foo`, meaning that it recognizes `my_obj` as an instance of `Derived`. So the reference *does* work. – Kyle Strand Mar 06 '15 at 19:44