Namespaces
Variants
Views
Actions

Pointer declaration

From cppreference.com
< cpp‎ | language
Revision as of 11:13, 21 February 2014 by Cubbi (Talk | contribs)

Declares a variable of a pointer or pointer-to-member type.

Contents

Syntax

A pointer declaration is any simple declaration whose declarator has the form

* attr(optional) cv(optional) declarator (1)
nested-name-specifier * attr(optional) cv(optional) declarator (2)
1) Pointer declarator: the declaration S * D; declares D as a pointer to the type determined by decl-specifier-seq S.
2) Pointer to member declarator: the declaration S C::*D; declares D as a pointer to member of C of type determined by decl-specifier-seq S.
declarator - any declarator other than a reference declarator (there are no pointers to references). It can be another pointer declarator (pointer to pointers are allowed)
attr(C++11) - optional list of attributes
cv - const/volatile qualification which apply to the pointer that is being declared (not to the pointed-to type, whose qualifications are part of decl-specifier-seq)
nested-name-specifier - a sequence of names and scope resolution operators ::

There are no pointers to references and there are no pointers to bit fields.

Pointers to objects

A pointer to object can be initialized with the return value of the address-of operator applied to any expression of object type, including another pointer type:

int n;
int* np = &n; // pointer to int
int* const * npp = &np; // non-const pointer to const pointer to non-const int
 
int a[2];
int (*ap)[2] = &a; // pointer to array of int
 
struct S { int n; };
S s = {1};
int* sp = &s.n; // pointer to the int that is a member of s

Pointers may appear as operands to the built-in indirection operator (unary operator*), which returns the lvalue identifying the pointed-to object:

int n;
int* p = &n;     // pointer p is pointing to n
int& ref = *p;   // the lvalue expression that identifies n is bound to a reference
ref = 7;         // stores 7 in n
std::cout << *p; // lvalue-to-rvalue conversion reads the value from n

Pointers to class objects may also appear as the left-hand operands of the member access through pointer operators operator-> and operator->*

Because of the array-to-pointer implicit conversion, pointer to the first element of an array can be initialized with an expression of array type:

int a[2];
int *p = a; // pointer to a[0]
 
int b[3][3];
int (*row)[3] = b; // pointer to b[0]

Because of the derived-to-base implicit conversion for pointers, pointer to a base class can be initialized with the address of a derived class.

struct Base {};
struct Derived : Base {};
 
Derived d;
Base* p = &d;

If Derived is polymorphic, such pointer may be used to make virtual function calls.

Certain arithmetic operators are defined for pointers to elements of arrays: such pointers satisfy the RandomAccessIterator requirements and allow the C++ library algorithms to work with raw arrays.

Pointers to functions

A pointer to function can be initialized with an address of a non-member function or a static member function. Because of the function-to-pointer conversion, the address-of operator is optional:

void f(int);
void (*pf1)(int) = &f;
void (*pf2)(int) = f; // same as &f

Unlike functions or references to functions, pointers to functions are objects and thus can be stored in arrays, copied, assigned, etc.

A pointer to function can be used on the left-hand side of the function call operator, this invokes the pointed-to function.

#include <iostream>
 
int f(int n)
{
    std::cout << n << '\n';
    return n*n;
}
int main()
{
    int (*p)(int) = f;
    int x = p(7);
}

Dereferencing a function pointer yields the lvalue identifying the pointed-to function:

int f();
int (*p)() = f;    // pointer p is pointing to f
int (&ref)() = *p; // the lvalue that identifies f is bound to a reference
ref();  // function f invoked through lvalue reference
(*p)(); // function f invoked through the function lvalue
p();    // function f invoked directly through the pointer

A pointer to function may be initialized from an overload set which may include functions, function template specializations, and function templates, if only one overload matches the type of the pointer (see address of an overloaded function for more detail)

template<typename T> T f(T n) {}
double f(double n) {}
 
int main()
{
    int (*p)(int) = f; // instantiates and selects f<int>
}

Pointers to data members

A pointer to non-static data member m which is a member of class C can be initialized with the expression &C::m exactly. Expressions such as &(C::m) or &m inside C's member function do not form pointers to members.

Such pointer may be used as the right-hand argument of the pointer-to-member access operators operator.* and operator->*.

struct C {
    int m;
};
int main()
{
    int C::*p = &C::m; // p points at member m of class C
    C c = {7};
    std::cout << c.*p << '\n'; // prints 7
    C* cptr = &c;
    cptr->m = 10;
    std::cout << cptr->*p << '\n'; // prints 10
}

Pointer to member of a base class can be implicitly converted to pointer to the same member of a derived class.

struct Base {
    int m;
};
struct Derived : Base {
};
int main()
{
    int Base::* pb = &Base::m;
    int Derived::* pd = pb;
    Derived d;
    d.m = 1;
    std::cout << d.*pd << ' ' << d.*pb << '\n'; // prints 1 1
}

Conversion in the opposite direction, from a pointer to member of a derived class to a pointer to member of an unambiguous non-virtual base class, is allowed with static_cast and explicit cast, even if the base class does not have that member (but the most-derived class does, when the pointer is used for access)

struct Base {
};
struct Derived : Base {
    int m;
};
int main()
{
    int Derived::* pd = &Derived::m;
    int Base::* pb = static_cast<int Base::*>(pd);
 
    Derived d;
    d.m = 7;
    std::cout << d.*pb << '\n'; // OK: prints 7
 
    Base b;
    std::cout << b.*pb << '\n'; // undefined behavior
}

The pointed-to type of a pointer-to-member may be a pointer-to-member itself: pointers to members can be multilevel, and can be cv-qualifed differently at every level. Mixed multi-level combinations of pointers and pointers to members are also allowed

struct A {
    int m;
    // const pointer to non-const member
    int A::* const p;
};
int main()
{
    // non-const pointer to member which is a const pointer to non-const member
    int A::* const A::* p1 = &A::p;
 
    const A a = {1, &A::m};
    std::cout << a.*(a.*p1) << '\n'; // prints 1
 
    // regular non-const pointer to a const pointer-to-member
    int A::* const * p2 = &a.p;
    std::cout << a.**p2 << '\n'; // prints 1
}

Pointers to member functions

A pointer to non-static member function f which is a member of class C can be initialized with the expression &C::f exactly. Expressions such as &(C::f) or &f inside C's member function do not form pointers to member functions.

Such pointer may be used as the right-hand argument of the pointer-to-member access operators operator.* and operator->*. The resulting expression can be used only as the left-hand operand of a function-call operator:

struct C {
    void f(int n) { std::cout << n << '\n'; }
};
int main()
{
    void (C::*p)(int) = &C::f; // p points at member f of class C
    C c;
    (c.*p)(1); // prints 1
    C* cptr = &c;
    (cptr->*p)(2); // prints 2
}


Pointer to member function of a base class can be implicitly converted to pointer to the same member function of a derived class.

struct Base {
    void f(int n) { std::cout << n << '\n'; }
};
struct Derived : Base {
};
int main()
{
    void (Base::* pb)(int) = &Base::f;
    void (Derived::* pd)(int) = pb;
    Derived d;
    (d.*pd)(1);
    (d.*pb)(2);
}

Conversion in the opposite direction, from a pointer to member function of a derived class to a pointer to member function of an unambiguous non-virtual base class, is allowed with static_cast and explicit cast, even if the base class does not have that member function (but the most-derived class does, when the pointer is used for access)

struct Base {
};
struct Derived : Base {
    void f(int n) { std::cout << n << '\n'; }
};
int main()
{
    void (Derived::* pd)(int) = &Derived::f;
    void (Base::* pb)(int) = static_cast<void (Base::*)(int)>(pd);
 
    Derived d;
    (d.*pb)(1); // OK, prints 1
 
    Base b;
    (b.*pb)(2); // Undefined behavior
}

Pointers to member functions may be used as callbacks or as function objects, often after applying std::mem_fn or std::bind.

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
int main()
{
    std::vector<std::string> v = {"a", "ab", "abc"};
    std::vector<std::size_t> l;
    transform(v.begin(), v.end(), std::back_inserter(l),
                std::mem_fn(&std::string::size));
    for(std::size_t n : l)
        std::cout << n << ' ';
}

Output:

1 2 3

Pointers to void

Pointer to object of any type can be implicitly converted to pointer to void (optionally cv-qualified). The reverse conversion, which requires static_cast or explicit cast, yields the original pointer value:

int n = 1;
int* p = &n;
void* pv = p;
 
int* p2 = static_cast<int*>(pv);
std::cout << *p2 << '\n'; // prints 1

If the original pointer is pointing to a base class subobject within an object of some polymorphic type, dynamic_cast may be used to obtain a void* that is pointing at the complete object of the most derived type.

Pointers to void are used to pass objects of unknown type, which is common in C interfaces: std::malloc returns void*, std::qsort expects a user-provided callback that accepts two const void* arguments. pthread_create expects a user-provided callback that accepts and returns void*. In all cases, it is the caller's responsibility to cast the pointer to the correct type before use.

Null pointers

Pointers of every type have a special value known as null pointer value of that type. A pointer whose value is null does not point to an object or a function (dereferencing a null pointer is undefined behavior), and compares equal to all pointers of the same type whose value is also null.

To initialize a pointer to null or to assign the null value to an existing pointer, the null pointer literal nullptr, the null pointer constant NULL, or the implicit conversion from the integer value 0 may be used.

Zero- and value-initialization also initialize pointers to their null values.

Null pointers can be used to indicate the absence of an object (e.g. function::target()), or as other error condition indicators (e.g. dynamic_cast). In general, a function that receives a pointer argument almost always needs to check if the value is null and handle that case differently (for example, the delete expression does nothing when a null pointer is passed)

Constness

  • If cv appears before * in the pointer declaration, it is part of decl-specifier-seq and applies to the the pointed-to object.
  • If cv appears after * in the pointer declaration, it is part of declarator and applies to the pointer that's being declared.
Syntax meaning
const T * pointer to constant object
T const * pointer to constant object
T * const constant pointer to object
const T * const constant pointer to constant object
// pc is a non-const pointer to const int
// cpc is a const pointer to const int
// ppc is a non-const pointer to non-const pointer to const int
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
// p is a non-const pointer to non-const int
// cp is a const pointer to non-const int
int i, *p, *const cp = &i;
 
i = ci;    // okay, value of const int copied into non-const int
*cp = ci;  // okay, non-const int pointed-to by cp can be changed
pc++;      // okay, non-const pointer (to const int) can be changed
pc = cpc;  // okay, non-const pointer (to const int) can be changed
pc = p;    // okay, non-const pointer (to const int) can be changed
ppc = &pc; // okay, address of a pointer to const is a pointer to pointer to const
 
ci = 1;   // error: const int cannot be changed
ci++;     // error: const int cannot be changed
*pc = 2;  // error: the pointed-to const int cannot be changed
cp = &ci; // error: const pointer (to non-const int) cannot be changed
cpc++;    // error: const pointer (to whatever) cannot be changed
p = pc;   // error: pointer to non-const cannot be pointed to a const object
ppc = &p; // error: pointer to pointer to const cannot be pointed
          //  to pointer to non-const

In general, implicit conversion from one multi-level pointer to another follows the rules described in qualification conversions.

Example

#include <iostream>
 
struct Class
{
    int data_member;
 
    void member_function(int i) const
    {
        std::cout << "Member function: " << i << std::endl;
    }
 
    static void static_member_function ( int i )
    {
        std::cout << "Static member function: " << i << std::endl;
    }
};
 
void global_function ( int i )
{
        std::cout << "Global function: " << i << std::endl;
}
 
int main()
{
    Class object;
 
    Class *ptr = &object;
    ptr->member_function(1);
 
    Class &ref = object;
    ref.data_member = 2;
    std::cout << "object.data_member = " << object.data_member << std::endl;
 
    void (Class::*ptr_to_memfun)(int) const = &Class::member_function;
    (object.*ptr_to_memfun)(3);
    (ptr->*ptr_to_memfun)(4);
 
    void (*ptr_to_fun)(int) = Class::static_member_function;
    ptr_to_fun(5);
 
    ptr_to_fun = global_function;
    ptr_to_fun(6);
 
}

Output:

Member function: 1
object.data_member = 2
Member function: 3
Member function: 4
Static member function: 5
Global function: 6