0

In the example below I have a base and a derived class, both almost identical but the derived overrides a function. The base class has a large member so I want to conserve memory usage by only creating a derived class object and then cast that to a base object which therefore will call the non-overridden function.

However, as soon as the cast'ed object is passed into InitBuffer() the object appears as it's original class. I suppose this doesn't really surprise me (since it's really just passing a memory pointer to the object) but I can't figure out how to "fully cast" so that it works the way I want. Is this possible?

class BaseClass
{
public:
   virtual void InitBuffer(void) { /* ...do base stuff... */ }
private:
   char BigBuffer[1024*1024];
};

class DerivedClass : public BaseClass
{
public:
   virtual void InitBuffer(void) { /* ...do derived stuff... */ }
};

void DoSomething(BaseClass &MyObject)
{
   MyObject.InitBuffer();
}

void NotWorkingFunctionality(void)
{
   DerivedClass  DerivedObj;  /* Reuse this object for both cases */
   volatile bool IsFoo = true;

   if (IsFoo)
      /* I want this to end up calling BaseClass's InitBuffer */
      DoSomething(dynamic_cast<BaseClass &>(DerivedObj)); /* Doesn't work :( */
   else
      /* I want this to end up calling DerivedClass's InitBuffer */
      DoSomething(DerivedObj);  /* Works great! */
}
void DesiredFunctionality(void)
{
   DerivedClass  DerivedObj;
   BaseClass     BaseObj;   /* Problem: Twice the memory usage that I want!! */
   volatile bool IsFoo = true;

   if (IsFoo)
      DoSomething(BaseObj);    /* Works great! */
   else
      DoSomething(DerivedObj); /* Works great! */
}
barnie82
  • 131
  • 1
  • 9
  • 2
    Why is `InitBuffer()` virtual in the first place? And why not just have one class and `void InitBuffer(bool isFoo)` there for example? – Anton Savin Mar 23 '17 at 01:44
  • InitBuffer() is virtual because it needs to do things differently depending on the object type. The above is HEAVILY simplified to highlight only the bare minimum to get my question across (not sure if it does :P). For the specific example I could of course pass around a flag (isFoo) but I imagine there's a cleaner way using polymorphism. – barnie82 Mar 23 '17 at 01:50
  • "*The base class has a large member so I want to conserve memory usage by only creating a derived class object*" - since `DerivedClass` derives from `BaseClass`, it inherits all of `BaseClass`'s data members, including the large member, thus `DerivedClass` will be just as big, if not bigger, then `BaseClass`. If you really want to conserve memory, don't allocate the large member statically at compile-time to begin with, allocate it dynamically at runtime instead, such as in `InitBuffer()`, that way `BaseClass` and `DerivedClass` can allocate it differently. – Remy Lebeau Mar 23 '17 at 02:02
  • Also `DoSomething(dynamic_cast(DerivedObj));` is *identical* to `DoSomething(DerivedObj);` First off, the `dynamic_cast` is redundant, as evident by the second call that does not use it. Second, you are passing the exact same `BaseClass&` reference to `DoSomething()` whether you cast or not. If you really want to do different things based on the input type, overload `DoSomething()` to accept different types of input, like one that accepts a `BaseClass&` and another that accepts a `DerivedClass&`. – Remy Lebeau Mar 23 '17 at 02:02
  • I appreciate your input @RemyLebeau! Of course DerivedClass is just as big: my point is I don't want to allocate TWO objects, one for each call path. Yes, I'm passing the exact same object (that is why I'm asking this question lol). I don't have the luxury of malloc on this embedded system. – barnie82 Mar 23 '17 at 15:43

1 Answers1

4

In C++, virtual methods and their overrides are implemented as vtables. A vtable is effectively a table of function pointers to use for each virtual function. This allows subclasses to have their methods point to other implementation.

A pointer or reference cast does not change the content of the vtable. Calling InitBuffer is translated as a virtual function call to whatever the current instance implements InitBuffer as.

What can you do? If you are calling it externally, as your example shows, see the answer to this question:

myObject.BaseClass::InitBuffer();

From within a sub-class method, you would simply do:

BaseClass::InitBuffer();
Community
  • 1
  • 1
EyasSH
  • 3,679
  • 22
  • 36
  • Really well explained @EyasSH! Now that I know this, I realize my question should be: Is there a way to "down cast" the objects vtables? I'm guessing no, as I imagine you would have mentioned that then :) Calling the way you suggest isn't really feasible as I would have to pass around a flag throughout the code with if/else everywhere. – barnie82 Mar 23 '17 at 15:59
  • @barnie82: There isn't a way to cast a vtable, for good reason. That would violate the principles of subtyping-based polymorphism (see [this article](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping).) – EyasSH Mar 23 '17 at 17:09
  • Relevant questions are: (1) Does your code actually need to create a sub-type with an override? (2) Who decides which method is called? Why is this happening by the caller? (3) Can the implementing function itself make that decision? – EyasSH Mar 23 '17 at 17:09