次のコードを実行して、2 つを比較しました。予想通り、仮想関数はわずかに高速でした (理由は後で説明します)。
#include <stdio.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
class Object
{
public:
enum Type
{
BUTTON,
NOT_BUTTON,
WIDGET_ABC
};
Object(Type type);
virtual void renderVirtual() = 0;
void renderSwitch();
int counter;
private:
void renderButton();
void renderNotButton();
void renderWidgetAbc();
Type type;
};
class Button : public Object
{
public:
Button();
virtual void renderVirtual();
};
class NotButton : public Object
{
public:
NotButton();
virtual void renderVirtual();
};
class WidgetAbc : public Object
{
public:
WidgetAbc();
virtual void renderVirtual();
};
Object::Object(Type type)
:type(type),
counter(0)
{
}
void Object::renderSwitch()
{
switch(type)
{
case BUTTON:
renderButton();
break;
case NOT_BUTTON:
renderNotButton();
break;
case WIDGET_ABC:
renderWidgetAbc();
break;
}
}
void Object::renderButton()
{
counter += 1;
}
void Object::renderNotButton()
{
counter += 2;
}
void Object::renderWidgetAbc()
{
counter += 3;
}
Button::Button()
:Object(BUTTON)
{
}
void Button::renderVirtual()
{
counter += 1;
}
NotButton::NotButton()
:Object(NOT_BUTTON)
{
}
void NotButton::renderVirtual()
{
counter += 2;
}
WidgetAbc::WidgetAbc()
:Object(WIDGET_ABC)
{
}
void WidgetAbc::renderVirtual()
{
counter += 3;
}
static struct timeval start, end;
static long mtime, seconds, useconds;
static void startTime()
{
gettimeofday(&start, NULL);
}
static void printTimeDiff()
{
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
printf("Elapsed time: %ld milliseconds\n", mtime);
}
int main()
{
const int size = 10000000;
Object *button = new Button();
Object *notButton = new NotButton();
Object *widgetAbc = new WidgetAbc();
startTime();
for(int i = 0; i < size; i++)
{
button->renderVirtual();
notButton->renderVirtual();
widgetAbc->renderVirtual();
}
printf("Virtual Function:\n");
printTimeDiff();
printf("button counter = %d\n", button->counter);
printf("notButton counter = %d\n", notButton->counter);
printf("widgetAbc counter = %d\n", widgetAbc->counter);
startTime();
for(int i = 0; i < size; i++)
{
button->renderSwitch();
notButton->renderSwitch();
widgetAbc->renderSwitch();
}
printf("Switch Function:\n");
printTimeDiff();
printf("button counter = %d\n", button->counter);
printf("notButton counter = %d\n", notButton->counter);
printf("widgetAbc counter = %d\n", widgetAbc->counter);
return 0;
}
「g++ main.cpp」を使用してビルドしたところ、次の結果が得られました
Virtual Function
Elapsed time 132 milliseconds
button counter = 10000000
notButton counter = 20000000
widgetAbc counter = 30000000
Switch Function
Elapsed time 206 milliseconds
button counter = 20000000
notButton counter = 40000000
widgetAbc counter = 60000000
次に、(最適化のために) -02 を追加してビルドすると、次の結果が得られました。
Virtual Function
Elapsed time 58 milliseconds
button counter = 10000000
notButton counter = 20000000
widgetAbc counter = 30000000
Switch Function
Elapsed time 76 milliseconds
button counter = 20000000
notButton counter = 40000000
widgetAbc counter = 60000000
どちらの場合も、仮想関数の方が高速でした。
仮想関数は非仮想関数よりも低速ですが、オーバーヘッドは最小限です。仮想関数は、関数ポインターである可能性が最も高いです (ただし、コンパイラーは別の方法で行うことができます)。したがって、仮想関数を呼び出す場合、余分なオーバーヘッドはポインターの逆参照だけです。以下は、仮想呼び出しに対してコンパイラが実行できることの例です。コンパイラはもう少しエレガントにそれを行うことができますが、アイデアを得ることができます.
#include <stdio.h>
class Object
{
public:
// function pointer acting as virtual function call
void (*funcPtr) (void *this_ptr);
};
class Button : public Object
{
public:
Button();
static void virtualFunc(void *this_ptr);
int counter;
};
Button::Button()
:counter(0)
{
// set object function pointer to our "virtual function"
funcPtr = &Button::virtualFunc;
}
void Button::virtualFunc(void *this_ptr)
{
Button *button_ptr = reinterpret_cast<Button*>(this_ptr);
button_ptr->counter++;
}
int main()
{
Object *button = new Button();
// virtual call using a function pointer
button->funcPtr(button);
printf("button counter = %d\n", static_cast<Button*>(button)->counter);
return 0;
}