C++ Pointers



Preamble

Pointers from C++11 have been reshaped with notably the introduction of smart pointers. However, smart pointers have been subject to some change (recognized obsolescence) in C++14, C++17…

Headlines
nullptr

Rule(s)

Example NULL_PTR.Cpp.zip 

int i = 0;
i++;
int* my_pointer_on_an_int_variable = &i; // Memory address of 'i'
std::cout << *my_pointer_on_an_int_variable << '\n'; // '1' is displayed...
int& j = i;
std::cout << j << '\n'; // '1' is displayed...
my_pointer_on_an_int_variable = NULL; // '#include <cstddef>' may be omitted to access 'NULL'
std::cout << *my_pointer_on_an_int_variable << '\n'; // Ooops!

Example NULL_PTR.Cpp.zip 

template<typename F, typename P> void gf(F f, P p) { // 'gf' generic function
    f(p); // Call of 'f' with 'p' as parameter...
}

void g(void* pointer) {
    std::cout << "'g' function is called...\n"; // '#include <iostream>'
}

int main(/* int argc, char** argv */) {
    g(NULL); // Fine
    g(0); // This works because 'NULL == 0' from C, but not so fine...

    gf(g, nullptr); // Fine
    // gf(g, NULL); // Compilation error comes from type checking: type of 'NULL' is not 'void*' 

    return 0;
}

See also

Redefining new and delete

Example Smart_pointers.Cpp.zip 

// '.h' file:
class Elephant : public Elephantidae {
    static void* _Preallocated_memory;
    …
public:
    void* operator new(size_t) noexcept(false);
    void operator delete(void*) noexcept(true);
    …
};

// '.cpp' file(s):
void* Elephant::_Preallocated_memory = nullptr;

void* Elephant::operator new(size_t size) noexcept(false){
    if (_Preallocated_memory) return _Preallocated_memory;
    void* p = std::malloc(size);
    // Alternative (caution: if 'new' is *inherited* then the allocated size is wrong):
    // void* p = ::new Elephant(); // Call of global 'new' to avoid recursion...
    // Solutions: (1) use 'final' for blocking inheritance from 'Elephant', or (2) use
    // private inheritance, i.e., 'class African_elephant : private Elephant',
    // which prevents undesired 'new' inheritance: 'African_elephant* ae = new African_elephant;' -> compilation error
    if (!p) throw std::bad_alloc(); // <=> 'p == nullptr' and 'ptr == NULL'
    return p;
}

void Elephant::operator delete(void* p) noexcept(true) {
    if (_Preallocated_memory) std::free(_Preallocated_memory);
    // Alternative:
    // if (_Preallocated_memory) ::delete _Preallocated_memory; // Call of global 'delete' to avoid recursion...
    _Preallocated_memory = p; // Recycle...
}
…
Elephant* Babar = new Elephant;
delete Babar;
Elephant* Elmer = new Elephant;
delete Elmer;
assert(Babar == Elmer);
// Before terminating, please release pool, if any, pointed by 'Elephant::_Preallocated_memory'

Smart pointers

Rule(s)

Example (principle) Smart_pointers.Cpp.zip 

// Modern C++ allows the allocation of pointers *without* the need of user-defined deletions:
std::unique_ptr<Mammoth> m0(new Mammoth("m0")); // '#include <memory>'
// std::unique_ptr<Mammoth> bug(m0.get()); // Hell and damnation! This may work only if 'm0.release();' occurs before resource destruction...
std::unique_ptr<Mammoth> m1 = std::make_unique<Mammoth>("m1"); 
// std::unique_ptr<Mammoth> m2 = m1; // Compilation error...
Mammoth m = *m1; // '*' operator overridden within 'std::unique_ptr'
Mammoth* pm = m1.get(); // Get the managed object, caution: double pointing!
assert(pm != nullptr);
pm = m1.release();
assert(m1.get() == nullptr); // Single pointing, i.e., 'pm'

Example (sharing) Smart_pointers.Cpp.zip 

std::shared_ptr<Mammoth> ma;
{
    std::shared_ptr<Mammoth> mb = std::make_shared<Mammoth>();
    ma = mb; // In essence, no compilation error...
    assert(ma.use_count() == 2);
} // No delete on 'mb' since shared with 'ma'
assert(ma.use_count() == 1);

Example (casting) Smart_pointers.Cpp.zip 

// '.h' file:
class Elephantidae { // 'Elephant' and 'Mammoth' as subtypes...
private:
    std::list<std::shared_ptr<Elephantidae> > _family_members;
protected:
    virtual ~Elephantidae() = default; // This makes 'Elephantidae' polymorphic to be used with 'dynamic_cast' *AND* 'dynamic_pointer_cast'
public:
    void push_family_member(const std::shared_ptr<Elephantidae>&);
    std::shared_ptr<Elephantidae> peek_family_member() const;
};

// '.cpp' file(s):
std::shared_ptr<Elephantidae> Jumbo = std::make_shared<Elephant>();
// 'Jumbo' is passed by reference (note that passing by copy would increment the number of pointers returned by 'use_count'):
ma->push_family_member(Jumbo); // Overridden '->' operator in 'std::shared_ptr'
// std::shared_ptr<Elephant> pe = ma->peek_family_member(); // Compilation error...
std::shared_ptr<Elephant> pe = std::dynamic_pointer_cast<Elephant>(ma->peek_family_member());
assert(pe);

Example (weak sharing) Smart_pointers.Cpp.zip 

std::weak_ptr<Mammoth> mx; // Weak pointer aims at replacing "raw" pointer obtained by 'get'...
{
    std::shared_ptr<Mammoth> my = std::make_shared<Mammoth>();
    mx = my;
    assert(my.use_count() == 1); // Weak pointer has no impact on the number of pointers returned by 'use_count'
    std::shared_ptr<Mammoth> mz = mx.lock(); // 'lock' copies into 'shared_ptr' object before usage...
    std::cout << mz.get() << std::endl;
} // Delete on 'my'
assert(mx.use_count() == 0);

Example (resource-based management) Smart_pointers.Cpp.zip 

std::unique_ptr<Mammoth> La_Madeleine(new Mammoth("Le mammouth gravé sur ivoire de La Madeleine (Tursac, Dordogne)"));
std::unique_ptr<Mammoth>&& La_Madeleine_is_moved = std::move(La_Madeleine);
assert(La_Madeleine.get() != nullptr && La_Madeleine.get() == La_Madeleine_is_moved.get());
La_Madeleine.release();
assert(La_Madeleine.get() == nullptr && La_Madeleine_is_moved.get() == nullptr);

std::unique_ptr<Mammoth> La_Grande_Liakhov(new Mammoth("La Grande Liakhov, l'île aux mammouths"));
Mammoth* La_Grande_Liakhov_resource = La_Grande_Liakhov.get();
std::unique_ptr<Mammoth> La_Grande_Liakhov_is_moved = std::move(La_Grande_Liakhov);
assert(La_Grande_Liakhov.get() == nullptr && La_Grande_Liakhov_resource == La_Grande_Liakhov_is_moved.get());
Function pointer

Rule(s)

Class member pointer

Rule(s)

Example Function_pointer.Cpp.zip 

// '.h' file:
class Meal {
public:
    static const std::tm Defaut_moment; // '#include <ctime>'
    enum Meal_type {
        Breakfast, Lunch, Afternoon_tea, Dinner
    };
private:
    enum Meal_type _type;
    std::tm _moment;
public:
    Meal(enum Meal_type = Breakfast, const std::tm& = Defaut_moment);
    const std::tm& moment() const;
    // It's useful to setup the type of the member function:
    typedef const std::tm& (Meal::*Moment_type)() const;
};

// '.cpp' file:
const std::tm Meal::Defaut_moment = {0, 0, 12, 15, 3, 96}; // April 15, 1996

Meal::Meal(enum Meal_type type, const std::tm& moment) : _type(type), _moment(moment) {}

const std::tm& Meal::moment() const {return _moment;}

int main(int argc, char** argv) {
    time_t clock = std::time(&clock);
    std::tm* now = std::gmtime(&clock);
    Meal m1(Meal::Dinner, *now);
    std::cout << "m1 at " << std::asctime(&(m1.moment())) << "\n\n";
    Meal m2;
    std::cout << "m2 at " << std::asctime(&(m2.moment())) << "\n\n";

    const std::tm& (Meal::*function_pointer)() const; // Pointer declaration
    function_pointer = &Meal::moment; // Pointer assignment
    const std::tm moment = (m1.*function_pointer)(); // Pointer use
    std::cout << "m1 at " << std::asctime(&(moment)) << "\n\n";

    char* (*pointer_asctime)(const std::tm*); // Pointer declaration
    pointer_asctime = &std::asctime; // Pointer assignment
    std::cout << "m2 at " << (*pointer_asctime)(&(m2.moment())) << "\n\n"; // Pointer use

    return 0;
}

Rule(s)

Example Function_pointer.Cpp.zip 

Meal::Moment_type function_pointer_ = function_pointer; // 'typedef const std::tm& (Meal::*Moment_type)() const;'
std::function<const std::tm& (const Meal&)> function_pointer__ = function_pointer;

Example N_INSEE.Cpp.zip 

// '.h' file:
class N_INSEE {
    // 'true' in all cases since 'sizeof(long long) >= 8':
    static_assert(sizeof(long long) > 4, "'sizeof(long) == 4' imposes the use of 'long long'");
    long long _valeur;
public:
    N_INSEE(const std::string&);
    int clef() const;
};

// '.cpp' file(s):
N_INSEE FB("1630125388055"); // Clef -> '29'
std::cout << "Key of 1630125388055 is equal to: " << FB.clef() << std::endl;
// long long N_INSEE::*p_valeur = &N_INSEE::_valeur; // Compilation error: pointers on class members require 'public:'
int (N_INSEE::*p_clef) () const = &N_INSEE::clef;
std::cout << "Key of 1010264445244 is equal to: " << std::invoke(p_clef, FB) << std::endl; // C++17: '#include <functional>'