oop - Why are member function pointers different from normal function pointers in C++? -
in beginning, there c.
, c had structure, , expressions, , functions package them. , good.
c had goto , switch case falling , syntax followed use, maybe not good.
it had pointers, causing gnashing of teeth aliasing , pointer arithmetic!
had function pointers, allowing run time dispatch, , rejoicing followed.
now, data dictate code, code dictating data, , both first class (or close).
pointed to, pointed to, same pointer: holy void*.
and equal in it's glory.
then c++ came, , bound data , code object.
, lo, syntactical sugar, function , method not different,
(no matter sun or oracle may tell you).
obj->foo(int val) being (approximately) same foo(obj* this, int val), still equal,
under holy void*.
and then, inheritance, came strife, outer derived class may add inner base.
lo, in these simple times, solution found: put every base before derived.
then, same pointer, may point both child , father.
and still, pointed to, pointed holy void*.
with virtual, lost our simplicity, , wandered ages.
wondering, how deal diamonds, or circles not quite ellipses.
cast off our old tenants of c, each instruction reducing simple asm,
embraced, things should simple (even if, under, complex).
and so, looked @ secret vtables arose , thought "good enough".
while had introduced hidden data, had reduced complexity.
, now, call made through virtual redirected through vtable.
, subobjects of class pointed through single pointer, enough.
but though dispatch method had changed, still point things, holy void*.
but made, many in present time, consider grave mistake: multiple inheritance.
, no longer 1 pointer suffice! how can both base fathers @ start?
, no longer, then, vtable suffice, how know subobject point to!
, now, problem of diamonds arises worse before, no obvious solution, , our prior ones requiring current code deal future prospects!
and so, pointer adjustments must made, each virtual call.
because base class may mi derived class in disguise, , needs adjusted.
support of choice few used mi, paid price.
and suddenly, holy void* no longer store had hither-forth been simple sugar.
, arose deal complexity, dreaded member function pointer.
the beast required it's own syntax, none others suffice.
, syntax, used, low in precedence require parans every use.
though in holy standard, wicked council decided allow such wretched things cast,
when cast type type, not called without invoking behavior undefined!
and nay, decadent syntax , greedy, fat , not fit void*.
way know object point to, adjustment,
nestled deep in pointer, , checked each vtable lookup.
but this, brothers, not how must be.
complexity of implementation comes peculiar of decisions.
class base1 { public: virtual void foo(); }; class base2 { public: virtual void bar(); }; class derived: public base1, public base2 { public: void unrelated(); } as seen here, derived* must adjusted when calling foo() or bar(); cannot point base1 , base2 @ same time, in case of simple single inheritance. is, indeed, impossible predict how of offset needed, when called base class, why have sort of mechanism adding vtable.
however:
class derived: public base1, public base2 { public: void unrelated(); virtual void foo() { base1::foo(); } virtual void bar() { base2::bar(); } } solves problem, nary change original object model!
each method exists, can added vtable, , when called, knows how adjust pointer, allowing call proceed without mucking about!
, now, both casting member function pointer calling when cast, defined!
and importantly of all, can pointed to, can pointed holy void*.
member function pointer need normal function pointer takes special first parameter.
, lo, things have been pretty good.
if lived in such dream world.
unfortunately us, live fat member function pointers, vtables have adjust each , every call, , member function pointers cannot appropriately create delegates or many other useful patterns. in msvc, change size when cast them!
the problem great std::function can allocate memory dynamically in implementations, because of various problems outlined here. use of thunks unoverriden methods, have detailed, solve problem quite handily, , cost of few inlinable hidden methods, , vtable changes, along possible (but tiny) decrease in speed of virtual dispatch, in case function isn't overriden , can't inlined perfectly.
and tiny negligible increase in speed , space, we've created monstrosity in member function pointers, , influenced half dozen other languages not use multiple inheritance despite solved problems, castrated member function pointers in use, , made our delegates slower.
in fact, noted in the fastest possible delegates, current solution slows down every virtual invocation; forces checking, , memory usage, via fat pointers, single inheritance, must store data (or risk losing it, msvc member function pointers can). not in "pay if use, not if don't" philosophy of c++!
so, reiterate, why member function pointers different "loose" function pointers? there logical reason why not function pointers special calling convention, or argument "this"?
c++ has rule of "don't pay don't use," meaning normal operations shouldn't slowed down pay other language features programmer isn't using. while absolutely could make member function pointers same normal function pointers, overhead factoring in dynamic dispatch, different vtable offsets, different base object offsets , thunks, etc. introduce overhead normal function pointers, either due memory needed store information (larger size) or logic required dispatch (extra time , code generated). therefore, makes sense split function pointers , member function pointers apart separate types separate implementations.
hope helps!
Comments
Post a Comment