Keywords: C++ | pointer to member | class member pointer
Abstract: This article comprehensively explores the core concepts of pointer to member in C++, analyzing its syntax structure, operator usage, and practical application scenarios through detailed code examples. It demonstrates how member pointers enable data access abstraction, algorithm generalization, and data structure flexibility. Based on high-scoring Stack Overflow Q&A, the article systematically examines the key roles of member pointers in advanced programming techniques such as function parameter passing and intrusive list implementation, providing C++ developers with a practical guide to understanding this special pointer type.
Basic Syntax and Declaration of Pointer to Member
In C++, a pointer to member is a special pointer type that points to non-static members of a class (including data members and member functions), rather than to specific object instances. The declaration syntax uses the ::* operator, with the basic form: type ClassName::*pointerName = &ClassName::memberName. For example, for a Car class containing an int speed member, a pointer to speed can be declared: int Car::*pSpeed = &Car::speed. The key insight is that &Car::speed obtains the relative offset of the member within the class, not a concrete memory address, so this pointer cannot be used alone and must be combined with a specific object to access actual data.
Access Operators for Member Pointers
To access object members using a member pointer, the .* (for objects via dot operator) or ->* (for objects via pointer operator) operators are required. These operators have lower precedence than the function call operator, so parentheses are typically necessary. For example:
Car c1;
c1.speed = 1; // Direct access
cout << "speed is " << c1.speed << endl;
c1.*pSpeed = 2; // Access via pointer to member
cout << "speed is " << c1.speed << endl;
This access method provides an additional level of indirection, allowing code to dynamically select which member to access at runtime, enhancing program flexibility. Note that parentheses in c1.*pSpeed are essential because the .* operator has lower precedence than the assignment operator; without them, the expression c1.*pSpeed = 2 would be parsed as c1.*(pSpeed = 2), causing a compilation error.
Practical Application Scenarios
Although pointer to member is uncommon in everyday programming, it is highly useful in specific scenarios. A classic application is writing generic algorithms that can operate on different data members of a class without needing separate functions for each member. For instance, consider a fruit counting scenario:
class bowl {
public:
int apples;
int oranges;
};
int count_fruit(bowl * begin, bowl * end, int bowl::*fruit) {
int count = 0;
for (bowl * iterator = begin; iterator != end; ++iterator)
count += iterator->*fruit;
return count;
}
By passing &bowl::apples or &bowl::oranges as the fruit parameter, the count_fruit function can count apples or oranges respectively, avoiding the need to write two nearly identical functions count_apples and count_oranges. This design pattern is particularly effective when dealing with multiple similar data members, reducing code duplication and improving maintainability.
Pointer to Member Function and Pluggable Architectures
In addition to data member pointers, C++ supports pointers to member functions, which are valuable for implementing pluggable architectures or callback mechanisms. The declaration syntax is: return_type (ClassName::*pointerName)(parameter_list) = &ClassName::functionName. For example, an Apply function can be designed to add pre- and post-processing logic around a user-specified member function:
void Apply(SomeClass * c, void (SomeClass::*func)()) {
// Perform hefty pre-call processing
(c->*func)(); // Call user-specified function
// Perform hefty post-call processing
}
Here, parentheses in (c->*func)() are required because the ->* operator has lower precedence than the function call operator (). This pattern allows dynamic selection of member functions at runtime, providing a foundation for plugin systems, event handling, or strategy patterns.
Application in Intrusive Lists
Another advanced application is in the implementation of intrusive lists. Intrusive lists require list elements to contain pointers to the next element, and member pointers allow the list template to be unaware of the specific pointer names. For example:
struct apple {
int data;
apple * next;
};
template<typename E>
struct List {
List(E *E::*next_ptr) : head(0), next_ptr(next_ptr) { }
void add(E &e) {
e.*next_ptr = head; // Access next pointer via member pointer
head = &e;
}
E * head;
E *E::*next_ptr;
};
int main() {
List<apple> lst(&apple::next);
apple a;
lst.add(a);
}
In this example, the List template learns the location of the next pointer in the apple structure via the constructor parameter next_ptr, enabling access through e.*next_ptr in the add function. This design decouples the list implementation from specific element types, enhancing code generality and reusability.
Summary and Best Practices
Pointer to member is a powerful yet infrequently used feature in C++ that supports more flexible and generic programming patterns by providing indirect access to class members. Key applications include: 1) Writing generic algorithms that operate on different data members; 2) Implementing callback or plugin architectures based on member functions; 3) Building intrusive data structures. When using them, attention must be paid to operator precedence to ensure correct parenthesis usage. While member pointers increase code complexity, they are indispensable in scenarios requiring high abstraction and dynamic behavior. Developers should weigh their use based on specific needs, avoiding unnecessary obscurity in inappropriate contexts.