Sept. 22, 2010, 1:06 a.m.
posted by nogood
Item 16. Pointers to Member Functions Are Not PointersWhen you take the address of a non-static member function, you don't get an address; you get a pointer to member function.
class Shape {
public:
//...
void moveTo( Point newLocation );
bool validate() const;
virtual bool draw() const = 0;
//...
};
class Circle : public Shape {
//...
bool draw() const;
//...
};
//...
void (Shape::*mf1)( Point ) = &Shape::moveTo; // not a pointer
The declaration syntax of a pointer to member function is really no more difficult than that of a pointer to a regular function (which, admittedly, is bad enough as it is; see Dealing with Function and Array Declarators [17, 61]). As with pointers to data members, all that's necessary is to use classname::* rather than * to indicate that the function referred to is a member of classname. Unlike a regular pointer to function, though, a pointer to member function can refer to a const member function: bool (Shape::*mf2)() const = &Shape::validate; As with a pointer to data member, we need an object or pointer to an object in order to dereference a pointer to member function. In the case of a pointer to data member, we need to add the object's address to the member's offset (contained in the pointer to data member) in order to access the member. In the case of a pointer to member function, we need the object's address to use as (or to calculate; see Meaning of Pointer Comparison [28, 97]) the value of the this pointer for the function call and possibly for other reasons as well. Circle circ; Shape *pShape = ˆ (pShape->*mf2)(); // call Shape::validate (circ.*mf2)(); // call Shape::validate The ->* and .* operators must be parenthesized because they have lower precedence than the () operator, and we have to first find out what function to call before we call it! This is entirely analogous to the use of parentheses in an expression such as (a+b)*c, where we want to ensure that the lower-precedence addition is carried out before the higher-precedence multiplication. Note that there is no such thing as a "virtual" pointer to member function. Virtualness is a property of the member function itself, not the pointer that refers to it. mf2 = &Shape::draw; // draw is virtual (pShape->*mf2)(); // call Circle::draw This is one reason why a pointer to member function cannot be implemented, in general, as a simple pointer to function. The implementation of the pointer to member function must store within itself information as to whether the member function to which it refers is virtual or nonvirtual, information about where to find the appropriate virtual function table pointer (see The Compiler Puts Stuff in Classes [11, 37]), an offset to be added to or subtracted from the function's this pointer (see Meaning of Pointer Comparison [28, 97]), and possibly other information. A pointer to member function is commonly implemented as a small structure that contains this information, although many other implementations are also in use. Dereferencing and calling a pointer to member function usually involves examining the stored information and conditionally executing the appropriate virtual or nonvirtual function calling sequence. As with pointers to data members, pointers to member functions exhibit contravariance: there is a predefined conversion from a pointer to a member function of a base class to a pointer to a member function of a derived class, not the reverse. This makes sense if you consider that a base class member function will attempt to access only base class members through its this pointer whereas a derived class function may attempt to access members that are not present in the base class.
class B {
public:
void bset( int val ) { bval_ = val; }
private
int bval_;
};
class D : public B {
public:
void dset( int val ) { dval_ = val; }
private:
int dval_;
};
B b;
D d;
void (B::*f1)(int) = &D::dset; // error! derived func in base ptr
(b.*f1)(12); // oops! access nonexistent dval member!
void (D::*f2)(int) = &B::bset; // OK, base func in derived ptr
(d.*f2)(11); // OK, set inherited bval data member
|
- Comment