2

This compiles gracefully:

class dummy {
};

This complains about undefined reference to _sbrk:

class dummy {
    virtual ~dummy();
};

Why virtual method generates undefined reference to _sbrk?

I used to think that vtable is allocated somewhere statically and does not requre malloc.

Compiler: arm-none-eabi-gcc 8.0.0 with recent newlib. Compiled with -fno-rtti -fno-exceptions -fno-unwind-tables.

Test program (boot is like main):

class base {
public:
  virtual ~base();
};

class dummy : public base {
public:
  ~dummy();
};

base::~base() {
  __BKPT();
}

dummy::~dummy() {
  __BKPT();
}

extern "C" void _sbrk() {
  __BKPT();
}

void boot() {
  for(;;) {
    base b;
    dummy d;
  }

  return 0;
}
Piotr Jedyk
  • 361
  • 1
  • 10

3 Answers3

3

Derived classes could have their own delete operator. This feature is very rarely used - almost never in my experience. A virtual destructor allows the correct operator delete to be called when the delete expression is used (delete p).

The compiler certainly generated a virtual destructor-with-delete that calls the class specific operator delete, which in almost all cases happens to be the global operator delete (::operator delete) but that can also be overridden by a local operator defined in the class.

Because delete is never used (for that particular type), that destructor-with-delete automatically generated virtual function is never called, but it is still referenced in the vtable. Unless you have appropriate compiler and linker support, every virtual function is referenced in the vtable and the vtable is used at least in constructors of the class, so any constructed object is going to need every virtual function of that class.

If you have appropriate linker support, you can only bring in virtual functions that are named; other vtable entries can be null as they are never referenced.

EDIT: The standard quote

From [class.dtor]/12:

At the point of definition of a virtual destructor (including an implicit definition), the non-array deallocation function is determined as if for the expression delete this appearing in a non-virtual destructor of the destructor's class (see [expr.delete]). If the lookup fails or if the deallocation function has a deleted definition, the program is ill-formed. [ Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression ([class.free]). — end note ]

"virtual destructor" ... determined as if a "non virtual destructor" contained an expression is a verbiage that may be hard to decode: since the destructor is virtual, why are we talking about a virtual destructor? (I had to read the above standard text several times.)

Another way to view is it is, in term of a possible implementation (some implementations used to do exactly that):

Each destructor actually takes a bool parameter deallocate and compiler adds code:

if (deallocate)
  delete this;

just before the end of the body of the destructor, and all destructors are called with a false argument except the one of the complete object when the delete operator is invoked.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • When added `void operator delete(void *)` to the class, it compiled without `_sbrk` dependency. It is really great answer; explained with details ;-) – Piotr Jedyk Aug 20 '17 at 17:10
  • Upvoted you again, because this is quality stuff. Please stick to what you know, or be suitably humble (and check your facts) when venturing into new areas. Just use the brain God gave you. I flagged your other comments to the mods, which is why they have been deleted. – Paul Sanders Jul 09 '18 at 23:20
-1

I'm almost certain _sbrk is being called in because your usage of virtual functions has pulled in a path of code to throw an exception for a call to a 'pure virtual function'.

go peeking around in your map file and see what's calling _sbrk, what's calling it, etc. until you find the root.

see this post with more information: Declaring abstract class (pure virtual method) increase binary size substantially

p.s. vtables don't require dynamic memory allocation

Russ Schultz
  • 2,545
  • 20
  • 22
-2

I used to think that vtable is allocated somewhere statically and does not require malloc.

Not sure where you got that assumption from:

  • There's no guarantee on how vtables are implemented nor do the standards say that dynamic dispatch needs to be implemented using vtables at all. It's just the most common method.
  • I am missing any proof in your question that the _sbrk is actually being pulled in because the destructor is virtual

And, I really don't get the purpose of the question: If you want to use virtual methods, you need to have dynamic memory allocation functions anyhow.

tofro
  • 5,640
  • 14
  • 31
  • `If you want to use virtual methods, you need to have dynamic memory allocation`: using your phrase, "Not sure where you got that assumption from". Can you elaborate? This runs contrary to my own experience, at least on embedded systems. – Dan Aug 19 '17 at 15:05
  • @Dan Virtual methods make sense only with polymorphism - and polymorphism requires accessing a method through a *base class pointer* - otherwise (all statically allocated) you could simply live completely without virtual functions to the very same effect. – tofro Aug 19 '17 at 19:38
  • 2
    Nothing about virtual functions or polymorphism requires a heap , malloc, or sbrk – Russ Schultz Aug 19 '17 at 22:38