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
- Storage allocation or reuse
- Object construction
- Â ... Object is alive ...
- Object destruction
- 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 .
- 649