Article View: de.comp.lang.c++
Article #1697Re: Frage zu C++-Funktionsadresse
From: Dietmar Kuehl
Date: Tue, 27 Feb 1996 17:37
Date: Tue, 27 Feb 1996 17:37
179 lines
5669 bytes
5669 bytes
Hi,
Detlef Volk (detlef.volk@shl.tu-ilmenau.de) wrote:
: Ich moechte von der Funktion einer Klasse die Adresse in
: eine Tabelle ablegen. Ueber den Adressoperator & ist dies nicht moeglich,
: da ich die Funktion nicht 'static' machen moechte.
Die Adresse, die Du haben moechtest, ist ein 'Pointer-to-Member' und
ist als solcher nicht genau dasselbe wie ein "normaler"
Funktionspointer.
: Beispiel:
: im Header:
: class TBsp {
: void Fkt(void);
: void BspFkt(void);
: };
: im CPP-File:
: typedef void (* VOIDFKT)(void);
: ...
: void TBsp::Fkt(void) {
: VOIDFKT Beispiele[20];
: ...
: Beispiele[0]= &TBsp::BspFkt; <- Compilerfehler !
: ...
: }
'&TBsp::BspFkt' hat nicht den Typ 'VOIDFKT', sondern den Typ 'void
(TBsp::*)(void)'. Es gibt also einen Typkonflikt. Man kann die
Memberfunktionen mittels der Pointer-to-Member auch nicht ganz normal
aufrufen, sondern muss sie mit dem '.*' oder dem '->*' Operator
aufrufen, z.B.:
void f(TBsp &obj, void (TBsp::*fkt)(void))
{
(obj.*fkt)()
}
Ananlog wird '->*' verwendet, wenn man einen Pointer anstatt einem
Objekt oder einer Referenz hat. Der Grund fuer diese Konstruktion ist,
dass die Memberfunktionen einen impliziten 'this'-Pointer haben. Das
Objekt, auf dass 'this' zeigt, muss irgendwo angegeben werden.
: Das Problem besteht also darin, dass vom Compiler
: beim Adressoperator & die Adresse direkt in den Maschinencode ein-
: geschrieben wird, aber die kann der Compiler nicht erzeugen, da sie erst
: zur Laufzeit feststeht.
Das trifft es nicht ganz. Die Kombination von Pointer-to-Member (liegt
zur Compiler- bzw. Linkzeit fest) und Objekt (wird erst zur Laufzeit
bestimmt) ist erst zur Laufzeit vorhanden. Um das zu bestimmen, kann
man sich mit einer Klasse helfen, die einen Pointer-to-Member und ein
Objekt (oder einen Pointer auf ein solches) enthaelt. Oft sollen dann
noch Funktionen von verschiedenen Klassen aufgerufen werden (etwa wenn
ein Callback-Mechanismus implementiert wird). Um das auch noch in den
Griff zu bekommen, braucht man eine virtuelle Funktion, die den
entsprechenden Pointer-to-Member aufruft. Hier ist ein Beispiel, wie
man sowas implementieren koennte (leider ist das Beispiel etwas
laenglich; dafuer ist es komplett und laesst sich z.B. mit gcc-2.7.2
compilieren):
#include <iostream.h>
#include <cassert>
class CallbackRC; // this declaration is not really necessary...
class CallbackBase
{
friend class CallbackRC;
private:
int i_refCount; // Used by 'CallbackRC'
public:
CallbackBase(): i_refCount(0) {}
CallbackBase(CallbackBase const &): i_refCount(0) {}
~CallbackBase() { assert(i_refCount == 0); }
CallbackBase &operator= (CallbackBase const &) { return *this; }
virtual void invoke() = 0;
};
class CallbackRC
{
private:
CallbackBase *i_ptr;
public:
CallbackRC(CallbackBase *ptr = 0):
i_ptr(ptr)
{
assert((i_ptr == 0) || (i_ptr->i_refCount == 0));
if (i_ptr != 0)
i_ptr->i_refCount = 1;
}
CallbackRC(CallbackRC const &rc):
i_ptr(rc.i_ptr)
{
assert((i_ptr == 0) || (i_ptr->i_refCount > 0));
if (i_ptr != 0)
++(i_ptr->i_refCount);
}
~CallbackRC()
{
if (i_ptr != 0 && --(i_ptr->i_refCount) == 0)
delete i_ptr;
}
CallbackRC &operator= (CallbackRC const &rc)
{
if (this != &rc)
{
if (i_ptr != 0 && --(i_ptr->i_refCount) == 0)
delete i_ptr;
i_ptr = rc.i_ptr;
assert((i_ptr == 0) || (i_ptr->i_refCount > 0));
if (i_ptr != 0)
++(i_ptr->i_refCount);
}
return *this;
}
CallbackBase &operator* () const { return *i_ptr; }
CallbackBase *operator-> () const { return i_ptr; }
};
template <class T>
class Callback: public CallbackBase
{
private:
T i_obj;
void (T::*i_fkt)(void);
public:
Callback(void (T::*fkt)(void), T const &init = T()):
i_obj(init),
i_fkt(fkt)
{
}
void invoke() { (i_obj.*i_fkt)(); }
};
class Bsp1
{
public:
void fkt1(void) { cout << "Bsp1::fkt1()\n"; }
void fkt2(void) { cout << "Bsp1::fkt2()\n"; }
};
class Bsp2
{
public:
void fkt1(void) { cout << "Bsp2::fkt1()\n"; }
void fkt2(void) { cout << "Bsp2::fkt2()\n"; }
};
int main()
{
CallbackRC list[] =
{
CallbackRC(new Callback<Bsp1>(&Bsp1::fkt1)),
CallbackRC(new Callback<Bsp1>(&Bsp1::fkt2)),
CallbackRC(new Callback<Bsp2>(&Bsp2::fkt1)),
CallbackRC(new Callback<Bsp2>(&Bsp2::fkt2))
};
for (int i = 0; i < sizeof(list) / sizeof(CallbackRC); ++i)
list[i]->invoke();
return 0;
}
Zusaetzlich zu dem, was ich in diesem Artikel beschrieben habe, gibt es
noch Information in dem C++ FAQ in Section 18, etwa unter folgender URL:
<http://www.informatik.uni-konstanz.de/~kuehl/cpp/cppfaq.htm#Q111> (und
folgende FAQs).
--
dietmar.kuehl@uni-konstanz.de
http://www.informatik.uni-konstanz.de/~kuehl
I am a realistic optimist - that's why I appear to be slightly pessimistic
Technical Details
Message-ID:
<4gvfg4$39g@news.BelWue.DE>Path:
arc1-ca.pugleaf.net!news.belwue.de!horn!kuehlReferences:
<4gsia4$90m@link.rz.tu-ilmenau.de>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!news.belwue.de!horn!kuehl From: kuehl@horn.informatik.uni-konstanz.de (Dietmar Kuehl) Newsgroups: de.comp.lang.c++ Subject: Re: Frage zu C++-Funktionsadresse Date: 27 Feb 1996 17:37:08 GMT Organization: Fakultät für Mathematik und Informatik Message-ID: <4gvfg4$39g@news.BelWue.DE> References: <4gsia4$90m@link.rz.tu-ilmenau.de> Reply-To: dietmar.kuehl@uni-konstanz.de NNTP-Posting-Host: horn.informatik.uni-konstanz.de X-Newsreader: TIN [version 1.2 PL2]