Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
157 views
in Technique[技术] by (71.8m points)

c++ - Polymorphism Without Virtual Functions

I am trying to optimize the run time of my code and I was told that removing unnecessary virtual functions was the way to go. With that in mind I would still like to use inheritance to avoid unnecessary code bloat. I thought that if I simply redefined the functions I wanted and initialized different variable values I could get by with just downcasting to my derived class whenever I needed derived class specific behavior.

So I need a variable that identifies the type of class that I am dealing with so I can use a switch statement to downcast properly. I am using the following code to test this approach:

Classes.h

#pragma once

class A {
public:
    int type;
    static const int GetType() { return 0; }
    A() : type(0) {}
};

class B : public A {
public:
    int type;
    static const int GetType() { return 1; }
    B() : {type = 1}
};

Main.cpp

#include "Classes.h"
#include <iostream>
using std::cout;
using std::endl;
using std::getchar;

int main() {
    A *a = new B();
    cout << a->GetType() << endl;
    cout << a->type;
    getchar();
    return 0;
}

I get the output expected: 0 1

Question 1: Is there a better way to store type so that I do not need to waste memory for each instance of the object created (like the static keyword would allow)?

Question 2: Would it be more effective to put the switch statement in the function to decide that it should do based on the type value, or switch statement -> downcast then use a derived class specific function.

Question 3: Is there a better way to handle this that I am entirely overlooking that does not use virtual functions? For Example, should I just create an entirely new class that has many of the same variables

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Question 1: Is there a better way to store type so that I do not need to waste memory for each instance of the object created (like the static keyword would allow)?

There's the typeid() already enabled with RTTI, there's no need you implement that yourself in an error prone and unreliable way.

Question 2: Would it be more effective to put the switch statement in the function to decide that it should do based on the type value, or switch statement -> downcast then use a derived class specific function.

Certainly no! That's a heavy indicator of bad (sic!) class inheritance hierarchy design.

Question 3: Is there a better way to handle this that I am entirely overlooking that does not use virtual functions? For Example, should I just create an entirely new class that has many of the same variables

The typical way to realize polymorphism without usage of virtual functions is the CRTP (aka Static Polymorphism).
That's a widely used technique to avoid the overhead of virtual function tables when you don't really need them, and just want to adapt your specific needs (e.g. with small targets, where low memory overhead is crucial).

Given your example1, that would be something like this:

template<class Derived>
class A {
protected:
    int InternalGetType() { return 0; }
public:
    int GetType() { static_cast<Derived*>(this)->InternalGetType(); }
};

class B : public A<B> {
    friend class A<B>;
protected:
    int InternalGetType() { return 1; }
};

All binding will be done at compile time, and there's zero runtime overhead.
Also binding is safely guaranteed using the static_cast, that will throw compiler errors, if B doesn't actually inherits A<B>.


Note (almost disclaimer):

Don't use that pattern as a golden hammer! It has it's drawbacks also:

  • It's harder to provide abstract interfaces, and without prior type trait checks or concepts, you'll confuse your clients with hard to read compiler error messages at template instantiantion.

  • That's not applicable for plugin like architecture models, where you really want to have late binding, and modules loaded at runtime.

  • If you don't have really heavy restrictions regarding executable's code size and performance, it's not worth doing the extra work necessary. For most systems you can simply neglect the dispatch overhead done with virtual function defintions.


1)The semantics of GetType() isn't necessarily the best one, but well ...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...