RAII

mutable
class C {
    int member;

    void foo() const {
        member = 42;
    }
};
mutable
class C {
    mutable int member;

    void foo() const {
        member = 42;
    }
};

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    static Stack createEmpty() {
        Stack s;
        return s;
    } 
    static Stack createFromVector(const std::vector<int>& numbers) {
        Stack s;
        s.data = numbers;
        return s;
    }
    static Stack createFromExisting(const Stack& st) {
        return createFromVector(st.data);
    }
    
    ...
};
Stack s1 = Stack::createFromEmpty();
Stack s2 = Stack::createFromVector(v);
Stack s3 = Stack::createFromExisting(s2);

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    static Stack create() {
        Stack s;
        return s;
    } 
    static Stack create(const std::vector<int>& numbers) {
        Stack s;
        s.data = numbers;
        return s;
    }
    static Stack create(const Stack& st) {
        return createFromVector(st.data);
    }
    
    ...
};
Stack s1 = Stack::create();
Stack s2 = Stack::create(v);
Stack s3 = Stack::create(s2);

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    Stack() 
    {
    } 
    Stack(const std::vector<int>& numbers) {
        data = numbers;
    }
    Stack(const Stack& st) {
        data = st.data;
    }
    
    ...
};
Stack s1 = Stack();
Stack s2 = Stack(v);
Stack s3 = Stack(s2);

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    Stack() 
    {
    } 
    Stack(const std::vector<int>& numbers) {
        data = numbers;
    }
    Stack(const Stack& st) {
        data = st.data;
    }
    
    ...
};
Stack s1;
Stack s2{v};
Stack s3{s2};

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    Stack() 
    {
    } 
    Stack(const std::vector<int>& numbers) {
        data = numbers;
    }
    Stack(const Stack& st) {
        data = st.data;
    }
    
    ...
};
const Stack s1;
const Stack s2{v}; // error
const Stack s3{s2}; // error

Constructors

class Stack {
private:
    std::vector<int> data;
public:
    Stack() 
    {
    } 
    Stack(const std::vector<int>& numbers) :
        data{numbers}
    {
    }
    Stack(const Stack& st) : 
        data{st.data}
    {
    }
    
    ...
};
const Stack s1;
const Stack s2{v};
const Stack s3{s2};

Constructors

class ClassName {
    T1 member1;    
    T2 member2;
    T3 member3;
    ...
    TN memberN;

    ClassName(args) :
        member1{...},
        member2{...},
        member3{...},
        ...
        memberN{...}
    {
        ...
    }
};
auto c1 = ClassName{args};
auto c2 = ClassName(args);
ClassName c3 = {args};
ClassName c4{args};
ClassName c5(args);
new ClassName(args);
new ClassName{args};

Constructor

class ClassName {
    T1 member1;    
    T2 member2;
    T3 member3;
    ...
    TN memberN;

    ClassName() :
        member1{...},
        member2{...},
        member3{...},
        ...
        memberN{...}
    {
        ...
    }
};
ClassName c1;
auto c2 = ClassName{};
auto c3 = ClassName();
ClassName c4 = {};
ClassName c5{};
ClassName c6();
new ClassName;
new ClassName();
new ClassName{};

Default

class C {
    T1 member1;
    T1 member2;
    
    C(args) :
        member2{...}
        // member1
        // default-constructed
    {
    }
}

Constructor

Default

struct Cls {
    std::vector<int> v;
    int coeff;
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls()
    {
    }
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() :
        v{}
    {
    }
};

Implicitly defined

Constructor

Default

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
    
    Cls()
    {
    }
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
    
    Cls() :
        v{}
    {
    }
};

Implicitly defined

Constructor

Default

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
    
    Cls() = default;
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
    
    Cls()
    {
    }
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls(args) {...}
    
    Cls() :
        v{}
    {
    }
};

Explicitly defaulted

Constructor

Default

Explicitly deleted

struct Cls {
    std::vector<int> v;
    int coeff;
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls()
    {
    }
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() :
        v{}
    {
    }
};

Constructor

Default

Explicitly deleted

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() = delete;
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls()
    {
    }
};
struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() :
        v{}
    {
    }
};

Constructor

Default

Trivial

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() = default;
};
struct point2D {
    int x, y;
    
    point2D() = default;
};

non trivial

trivial

Constructor

Default

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() = default;
};
struct point2D {
    int x, y;
    
    point2D() = default;
};

no-throw default constructible

trivially default constructible

struct Cls {
    std::vector<int> v;
    int coeff;
    
    Cls() noexcept
        = default;
};

default constructible

#include <type_traits>
#include <type_traits>
#include <type_traits>
std::is_trivially_default_constructible_v<Cls>
std::is_nothrow_default_constructible_v<Cls>
std::is_default_constructible_v<Cls>

Constructor

struct Cls {
    std::vector<int> x;
    int y;
    
    // implicitly defined
};

Cls p1;
// p1.x == {}
// p1.y undefined
Cls p2{};
// p1.x == {}
// p1.y == 0

Default

struct Cls {
    std::vector<int> x;
    int y;
    
    Cls()
    {
    }
};

Cls p1;
// p1.x == {}
// p1.y undefined
Cls p2{};
// p1.x == {}
// p1.y undefined
struct Cls {
    std::vector<int> x;
    int y;
    
    Cls() : x{}, y{}
    {
    }
};

Cls p1;
// p1.x == {}
// p1.y == 0
Cls p2{};
// p1.x == {}
// p1.y == 0

Defaulted

default constructor

User defined default constructor

User defined default constructor

struct Cls {
    std::vector<int> x;
    int y;
    
    Cls() = default;
};

Cls p1;
// p1.x == {}
// p1.y undefined
Cls p2{};
// p1.x == {}
// p1.y == 0
point2D p1;
point2D p2{};

default initialization

value initialization

RAII and lifetime

  1. Storage allocation or reuse
  2. Object construction
  3.  ... Object is alive ...
  4. Object destruction
  5. Storage deallocation or reuse
{
    std::string s;
    // storage allocated on stack
    // std::string constructor invoked
    //    internal string buffer allocated
    //    size set to 0
}
// storage deallocated on stack
// std::string destructor invoked
//    internal string buffer deallocated

RAII

Resource Acquisition Is Initialization

new
delete
fopen
fclose
libcreate
libdestroy
lock
unlock

RAII

Resource Acquisition Is Initialization

setup1();
setup2();
setup3();

...
work();
...

teardown3();
teardown2();
// forgot to teardown1()!
setup();

...
work(); // exception thrown
...

teardown(); // not reached
setup();
bool cleaned = false;

...
try {
    work();
catch(...) {
    cleaned = true;
    teardown();
}
...

if (!cleaned) {
    teardown();
}
setup1();
setup2();
setup3();

...
work();
...

teardown3();
teardown2();
teardown1();
setup1();
setup2();
setup3();

...
work();
...

// wrong order
teardown1();
teardown2();
teardown3();
setup1();
setup2();
setup3();

...
work();
...

teardown3();
teardown2();
teardown1();

RAII

Resource Acquisition Is Initialization

new
delete
fopen
fclose
libcreate
libdestroy
lock
unlock

Constructor

Destructor

destructors

class Resource {
    Resource(args) {
        // construct
    }
    ~Resource() {
        // destruct
    }
};
{
    // Resource::Resource()
    Resource r;

    work(r);
} // Resource::~Resource()
ClassName(args)
~ClassName()

Constructor

Destructor

class SmartArray {
private:
    int* _data;
    size_t _size;
public:
    SmartArray(size_t size) :
        _data{new int[size]},
        _size{size}
    {
    }
    ~SmartArray() {
        delete[] _data;
    }

    int& at(int i) {
        return _data[i];
    }
    const int& at(int i) const {
        return _data[i];
    }
    size_t size() {
        return _size;
    }
};
{
    SmartArray ar{10};
    for (int i = 0; i < ar.size(); ++i) {
        ar.at(i) = i * i;
    }
} // SmartArray::~SmartArray()

destructors

Constructors

class C {
public:
    C();
    C(int arg1 = 42);
};

Default constructor

Copy constructor

Move constructor

Explicit constructor

class C {
public:
    C(const C&);
};
class C {
public:
    C(C&&);
};
class C {
public:
    explicit C(char);
};
C c1;
// C c1(); most vexing parse
C c2{};
new C;
...
C c3 = c1;
C c4{c1};
f(c1); // if by value
...
C c4 = createTemporary();
C c5 = std::move(c2);
C c1 = 'c'; // error
f('c'); // error (f takes C)
return 'c'; // error (returns C)
...

Constructor

explicit

class SmartArray {
private:
    int* _data;
    size_t _size;
public:
    SmartArray(size_t size) :
        _data{new int[size]},
        _size{size}
    {
    }
    ~SmartArray() {
        delete[] _data;
    }

    int& at(int i) {
        return _data[i];
    }
    const int& at(int i) const {
        return _data[i];
    }
    size_t size() {
        return _size;
    }
};

Explicit constructor

SmartArray f(const SmartArray& ar) {
    return 10;
}

void g() {
    SmartArray ar1 = static_cast<SmartArray>(10);
    SmartArray ar2 = 10;
    SmartArray ar3 = {10};
    f(10);
}

converting constructor

Constructor

explicit

class SmartArray {
private:
    int* _data;
    size_t _size;
public:
    explicit SmartArray(size_t size) :
        _data{new int[size]},
        _size{size}
    {
    }
    ~SmartArray() {
        delete[] _data;
    }

    int& at(int i) {
        return _data[i];
    }
    const int& at(int i) const {
        return _data[i];
    }
    size_t size() {
        return _size;
    }
};

Explicit constructor

SmartArray f(const SmartArray& ar) {
    return 10;
}

void g() {
    SmartArray ar1 = static_cast<SmartArray>(10);
    SmartArray ar2 = 10;
    SmartArray ar3 = {10};
    f(10);
}

explicit constructor

SmartArray ar = {2, 3, 4, 5, 7};
std::initializer_list
class SmartArray {
private:
    int* _data;
    size_t _size;
public:
    explicit SmartArray(size_t size) :
        _data{new int[size]},
        _size{size}
    {
    }
    ~SmartArray() {
        delete[] _data;
    }

    int& at(int i) {
        return _data[i];
    }
    const int& at(int i) const {
        return _data[i];
    }
    size_t size() {
        return _size;
    }
};
class SmartArray {
private:
    int* _data;
    size_t _size;
public:
    explicit SmartArray(size_t size) :
        _data{new int[size]},
        _size{size}
    {
    }

    SmartArray(std::initializer_list<int> il) :
        _data{new int[il.size()]},
        _size{il.size()}
    {
        std::copy_n(il.begin(), il.size(), _data);
    }

    ~SmartArray() {
        delete[] _data;
    }

    int& at(int i) {
        return _data[i];
    }
    const int& at(int i) const {
        return _data[i];
    }
    size_t size() {
        return _size;
    }
};
SmartArray ar = {2, 3, 4, 5, 7};
std::initializer_list
struct point2D {
    int x, y;
    
    point2D() :
        x{13},
        y{42}
    {
    }
}

in-class initializers

struct point2D {
    int x = 13, y = 42;
}

=

struct point2D {
    int x, y = 42;
    
    point2D() :
        x{13}
    {
        // y == 42
    }
}

in-class initializers

T obj;
T object {};
T()
T{}
new T()
new T{}
Class::Class(...) : member() { ... }
Class::Class(...) : member{} { ... }
T object(args...);
T object{arg};
T(args);
static_cast<T>(obj)
new T(args...)
Class::Class() : member(args...) { ... }
[arg](){ ... }
T object = other;
T object = {other};
f(other)
return other;
throw object;
catch (T object)
T array[N] = {other};
T object{args...};
T{args...}
new T{args...}
class { T member{args...}; }
Class::Class() : member{args...} { ... }
T object = {args...};
f({args...})
return {args...} ;
object[{args...}]
object = {args...}
U({args...})
Class { T member = {args...}; };
T object = {args...};
T object{args...};
T object = { .designator = arg1 , .designator { arg2 } ... };
T object { .designator = arg1 , .designator { arg2 } ... };
T object(args...);
T& ref = object;
T& ref = {args...};
T& ref(object);
T& ref{args...};
T&& ref = object;
T&& ref = {args...};
T&& ref(object) ;
T&& ref{args...};
f(object)
f({args...})
return object;
Class::Class(...) : ref(object) {...}

Default

Value

Direct

Copy

Direct list

Copy list

Aggregate

Reference

initialization

To be continued...

RAII

RAII

inheritance

virtual

  • constructor
  • member initializer list
  • destructor
  • default constructor
  • std::initializer_list
  • in-class initializer
  • copy ctor
  • cpy assign op
  • move ctor
  • mv assign op
  • default members
  • deleted members
  • rule of 0
  • rule of 3/5
  • smart-ptrs
  • converting constructor
  • explicit specifier
  • is-a relation
  • has-a relation
  • public inheritance
  • final specifier
  • using declaration
  • empty base optimization
  • protected members
  • private & protected inheritance
  • multiple inheritance
  • diamond problem
  • base method hiding problem
  • virtual methods
  • override specifier
  • dynamic type
    • RTTI
    • dynamic_cast
    • typeid
    • vtables
  • by value passing problem
  • pure virtual function
  • abstract class

misc

  • friends
  • nested members
  • conversion operators
  • operator overloading
  • member pointers
  • unions
    • std optional
    • std variant
    • std any
  • user defined literal

RAII

By Jan Bielak

RAII

A presentation about constructors, destructors and RAII. It is presented here: https://www.youtube.com/watch?v=yDrIHAS2JgY .

  • 500