1

I this a good implementation for this simple inheritance not using any virtual functions.

Why I would like this: Avoid virtual inheritance in performance crititcal code. A boost::variant could help here certainly, but I want only standard c++ stuff :-)

I refer also to this answer Link

#include <iostream>
using namespace std;

struct C;
struct B;

class A{
    public:
    A(int r): type(r){}
    int type;
    void deleter();
    protected:
    ~A(){std::cout << "DTOR A" << std::endl;}
};

class B: public A{
    public:
    B():A(1){};
    ~B(){std::cout << "DTOR B" << std::endl;}
};

class C: public A{
    public:
    C():A(2){};
    ~C(){std::cout << "DTOR B" << std::endl;}
};

void A::deleter(){
     if(type==1){
        delete static_cast<B* const>(this);
    }else if(type==2){
        delete static_cast<C* const>(this);
    }
}

int main(){
      A * a = new B();
      a->deleter();
}

I want to use the base pointer a. The main problem is what to do when we want to delete a. I simply prohibited that by making the DTOR of A protected. So the only way of using this construct is to call a.deleter() to safely delete a bas class pointer. Is this safe and a good implementation or is there a better approach?

http://ideone.com/pd88xy

Community
  • 1
  • 1
Gabriel
  • 8,990
  • 6
  • 57
  • 101
  • 2
    It seems you basically want someone to review your code. The Code Review Stackexchange might be a better choice then. – dandan78 Sep 30 '14 at 07:39
  • 1
    Please give an explanation why you are doing this. What do you want to achieve? – Siyuan Ren Sep 30 '14 at 07:39
  • 3
    Why do you want to invent your own RTTI-system? – Mats Petersson Sep 30 '14 at 07:39
  • I think it is safe, but a good implementation would use a `virtual` destructor. Alternatively, the instantiator of each object could retain type information until the deletion of the object, passing a base pointer for use to the rest of the program. That would be good even without `virtual`. – Magnus Hoff Sep 30 '14 at 07:39
  • 1
    Looks really hard to extend and error prone to me. The `deleter()` function in your base class must be aware of each subtype of `A` that exists. Can be fine if you're the only one to use your code, and even then, forgetting a case can still happen. Oh and why do you want to avoid the native mechanism for polymorphism? – JBL Sep 30 '14 at 07:42
  • I'll add that you also must call manually the deleter. RAII would be better... – JBL Sep 30 '14 at 07:44
  • I added some comments why I want this – Gabriel Sep 30 '14 at 08:14
  • Voted to close (I read your question two times - I still don't know what exactly you are asking). "Is this safe and a good implementation or is there a better approach?" - good/better approach to achieve what? (i.e. your code breaks the principle of least surprise, introduces dependecies in the base class, towards specializations, and imposes casts on the code); if there is a good reason for this, it is not visible in the code or in your question). – utnapistim Sep 30 '14 at 08:17
  • If you want to avoid virtual inheritance in performance crititcal code, you probably also want to avoid polymorphic behavior that cannot be statically inferred in the same code. A better approach is to restructure your code so it does not need to forget the real type of the object in the scope where the object is created and deleted. – Magnus Hoff Sep 30 '14 at 08:22
  • You do understand you are just reimplementing inheritance (very inefficiently)? – rubenvb Sep 30 '14 at 11:40
  • @rubenvb: I can't say whether Gabriel understands that or not, but I'd be interested to hear you explain your assertion as I can't understand what you're alluding to. Thanks in advance. – Tony Delroy Sep 30 '14 at 12:37
  • @MagnusHoff: (should be "virtual dispatch") solid point - there are times when that's true, but certainly not universally... in my measurements (long ago) for trivial functions like getters and setters of simple types, switching on a class id like this is about 10x faster than virtual dispatch due to the inline/out-of-line differences, which *may* make it worthwhile when what you suggest - which is effectively instantiating the outer algorithm for each type - produces unwanted code bloat or doesn't interact as well with code caching. – Tony Delroy Sep 30 '14 at 12:41
  • @TonyD (yes, "virtual dispatch") My main concern is that the code as it stands is hard to extend in the future (one has to know about and remember the `deleter` function), and statically knowing the types of objects would alleviate this. There are too many unknowns outside of the code here to analyse performance precisely, so I am going to refrain from that :) – Magnus Hoff Sep 30 '14 at 13:22
  • This question appears to be off-topic because it belongs on codereview.stackexchange.com – rubenvb Sep 30 '14 at 13:56
  • Yeah I completely understand, that in an OO-Design framework one definitely should not use this. But in performance critical code where one still wants to be able to dynamically replace the base pointer and where the base pointer calls functions (which should be dispatched to the underlying type) in a loop , then I think this thing is faster, and comes with the disadvantage of code bloat, harder maintenance etc... – Gabriel Sep 30 '14 at 14:50

1 Answers1

3

You've cited a guideline "Avoid virtual inheritance in performance crititcal code". It's not a bad guideline, but it isn't the best wording.

The real rule is "Avoid runtime polymorphism in performance crititcal code". Runtime polymorphism causes a performance hit. The compiler can't optimize across functions if it doesn't know the exact function being called ahead of time. (Branch prediction helps, but still is nowhere near as good as inlining-dependent optimizations)

To get both high performance and polymorphism, hoist the polymorphism outside of any loops. If the only polymorphic calls are for large operations, you don't pay the price of polymorphism very often.

Templates (compile-time polymorphism) can help compose the small operations without code duplication. Then you use a dynamic dispatch technique to pick one of the highly optimized template instantiations. At that point which dynamic dispatch technique you use doesn't much matter. Virtual calls, type erasure in std::function, or your own table of function pointers. Because the dynamic dispatch operation is small compared to the actual work.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720