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…
nullptr
Rule(s)
- From C++11, pointers benefit from being managed from
nullptr
value whose type isstd::nullptr_t
.- C++ pointers are concerned with primitive and non-primitive types. While
*
is the distinctive sign of pointers,&
is the distinctive sign of references. References are pointers that cannot be equal toNULL
ornullptr
.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
anddelete
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)
- Smart pointers have been standardized in C++11 while prior C++ only advocates best practices of smart pointers through template classes.
- Smart pointers rely on three key template classes:
std::unique_ptr
,std::shared_ptr
and,std::weak_ptr
(std::auto_ptr
in no longer supported in C++17).std::shared_ptr
comes with robust cast support:dynamic_pointer_cast
in particular.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());
Rule(s)
- C++ supports the notion of function pointer by which any function may be called through a pointer instead of its name. C++11 extends the principle by means of the
std::function
mechanism.Class member pointer
Rule(s)
- A class member pointer is a pointer on public non-static members: attributes or functions.
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)
- The way a class member pointer is accessed and next called is multifaceted.
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>'