If you use new, you must use delete — otherwise, memory leak! see code.
new[]:size must be provided as part of the signature.
1
2
3
newint[10]();// value-initialized to 0newint[5]{1,2,3};// OK: extra elements zero-initializednewint[]{1,2,3};// ❌ error: size must be explicit
new[] must match delete[]. A simple delete will yield undefined behaviour.
- delete will take not into account the number of objects, but delete[] will.
- delete[] should not be used on regular char*, unless it is char* c = new char[size_];.
Structs that contain pointers
Default-initialized pointers inside a struct are nullptr — but that means no memory is allocated. → You must new those inner pointers manually.
📌 Using Pointers
Always initialize your pointers! Uninitialized pointers cause random crashes or data corruption.
1
2
3
4
int*p;// Not initialized yet*p=10;// dangling pointer, segfault!// do this: int*q=nullptr
It’s more often to use int** ptr than int*& ptr
1
2
voidupdatePtr(int*&ptr);// UncommonvoidupdatePtr(int**ptr);// Common
int** ptr can take an rvalue reference (&some_tmp_ptr), but int *& needs the ptr to be lvalue reference. so it’s more flexible
In C, people have been using int**
After delete or free, set the pointer to nullptr. This avoids use-after-free or double-free errors.
1
2
deleteptr_;ptr_=nullptr;
Const type pointer is read-only pointer (const T pointer), while type* const is const pointer to T
1
2
3
4
5
6
7
8
9
10
// main.cpp: In function ‘void process(const int*)’:// main.cpp:13:10: error: assignment of read-only location ‘* ptr’voidprocess(constint*ptr){*ptr=9;}voidkeep_pointer_same_address(int*constptr){// ptr = ...; // ❌ cannot change ptr*ptr=5;// ✅ can change the int}
It’s recommended to add const specifier to raw pointers whenever possible.
int* const ptr → constant pointer to int
const int* ptr → pointer to constant int
const shared_ptr<T> ≈ T* const → shared_ptr can’t be reassigned, but pointee can be mutated
shared_ptr<const T> → pointer to const object
If you’re using shared_ptr<const T>, you must initialize in initializer list, especially for class members.
Do nullptr check unless you are 100% confident that the pointer is not Null
Always explicitly initialize to nullptr in constructors or field declarations.
Getting array size is unsafe: sizeof(int*) / sizeof(int); // ❌ not safe
Raw pointer fragility: shallow copy will point to the same memory address, which could be double-deleted.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classResource{int*data;public:Resource(){data=newint(42);}~Resource(){deletedata;}// Resource(const Resource& other) = default; // shallow copy// Resource& operator=(const Resource& other) = default; // shallow copy};voiddisasterExample(){Resourcer1;Resourcer2=r1;// when the function terminates, r1 and r2 will delete the same pointer, disaster!}
You must delete a pointer after use, and highly highly recommended to set it to nullptr
#include<iostream>usingnamespacestd;classB{//virtual void fun() {} // NEED TO ADD THIS!!};classD:publicB{};intmain(){B*b=newD;D*d=dynamic_cast<D*>(b);// 1. use dynamic_cast if we're not sure if we can succeedif(d!=NULL)cout<<"works";elsecout<<"cannot cast B* to D*";// 2. cpp still allows you to use static_ptr_caststd::static_pointer_cast<DerivedClass>(ptr_to_base)->f();// 3. even static_pointer_caststatic_cast<DerivedClass*>(ptr_to_base.get())->f();// equivalent to abovereturn0;}