Compiling C++ without the delete operator
Asked Answered
P

1

11

I am writing a C++ program for an embedded device, and I want to compile it without libstdc++, exceptions, and dynamic memory allocation.

Example program:

#include <stdio.h>

class A
{
public:
    virtual ~A() {}
    virtual void Foo() = 0;
};

class B : public A
{
public:
    virtual ~B() {}
    virtual void Foo() override{}
};

int main()
{
    B b;
    return 0;
}

Immediately I ran into the following errors.

$ gcc src.cpp -static -fno-rtti -fno-exceptions -std=c++11

/tmp/ccd0Wydq.o: In function A::~A()': src.cpp:(.text._ZN1AD2Ev[_ZN1AD5Ev]+0x29): undefined reference to operator delete(void*)' /tmp/ccd0Wydq.o: In function A::~A()': src.cpp:(.text._ZN1AD0Ev[_ZN1AD5Ev]+0x20): undefined reference to operator delete(void*)' /tmp/ccd0Wydq.o: In function B::~B()': src.cpp:(.text._ZN1BD2Ev[_ZN1BD5Ev]+0x35): undefined reference to operator delete(void*)' /tmp/ccd0Wydq.o: In function B::~B()': src.cpp:(.text._ZN1BD0Ev[_ZN1BD5Ev]+0x20): undefined reference to operator delete(void*)' /tmp/ccd0Wydq.o:(.rodata._ZTV1A[_ZTV1A]+0x20): undefined reference to `__cxa_pure_virtual' collect2: error: ld returned 1 exit status Makefile:2: recipe for target 'all' failed make: *** [all] Error 1

I understand why __cxa_pure_virtual is needed, but couldn't for the life of me get why I need a delete implementation.

I perform no new or delete operations in the code, why would it be needed?

When implementing both functions to satisfy the linker's demands, it seems that neither are called (as expected).

Is there a way to avoid implementing these functions?

Parley answered 22/8, 2016 at 16:47 Comment(4)
Is there something wrong with implementing them by having them do nothing?Emelia
I would stub them by a big assert(1==0) to be sure that they are not called.Beethoven
It's an ugly solution. I'll do it if it's the only solution, but I want to know why it's the case.Parley
Try adding also -flto or -fwhole-program, and -Wl,--as-needed.Frissell
H
12

When a virtual destructor is called via a delete expression, the operator delete that gets called is determined from the scope of the most-derived class. For example,

#include <iostream>

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

void destroy_base(Base* b) { delete b; }

class Derived : public Base {
public:
    static void operator delete(void* ptr) {
        std::cout << "Derived::operator delete\n";
        ::operator delete(ptr);
    }
};

int main() {
    destroy_base( new Derived );
}

prints "Derived::operator delete", even though function destroy_base has no knowledge of class Derived.

g++ implements this by putting two versions of the destructor in every class's vtable: one that just destroys the members and bases, and one that does all that and then calls the appropriate operator delete. This is where your undefined symbols are coming from.

If you never actually use a delete expression, just stub out the ::operator delete function and you should be fine.

Heading answered 22/8, 2016 at 17:3 Comment(4)
Is there a way to prevent the compiler from generating the second destructor? I am not interested in stubbing the delete operator.Parley
I don't believe so.Heading
@GiladNaaman, can't imagine how it would be possible. Standard requires that the proper delete is called, and when compiling the base class, compiler has no idea how it would be used - so it has to generate the destructor which calls operator delete.Infrequency
Thank you for the clarification, I'll write a stub, as suggested.Parley

© 2022 - 2024 — McMap. All rights reserved.