smart pointers

and inheritance

references and ownership

Returning values

Type func();
Type& func();
const Type& func();
Type* func();
const Type* func();
const Type func();
Type&& func();
const Type&& func();

Taking parameters

Readble Writable Transfers ownership
Yes Yes Yes
Yes Yes No
Yes No No
Yes Yes Depends
Yes No Depends
Yes No Yes
Yes Yes Yes
Yes No Yes
void func(Type);
void func(Type&);
void func(const Type&);
void func(Type&&);
void func(Type*);
void func(const Type*);
void func(const Type);
void func(const Type&&);
Reads Modifies Takes ownership
Yes No No
Depends Yes No
Yes No No
Yes Yes Yes
Depends Depends Depends
Yes No Depends
Yes No No
Yes No Yes

smart pointers

std::unique_ptr
std::shared_ptr
std::weak_ptr
unique_ptr
shared_ptr
weak_ptr
shared_ptr
weak_ptr

resource

resource

std::unique_ptr
std::unique_ptr<T>

std::unique_ptr<T>{new-allocated-pointer}
std::unique_ptr<T, DeleterT>{pointer, deleter}
std::make_unique<T>(initializer)
std::make_unique<T[]>(array-size)
std::make_unique_for_overwrite<T>()
std::make_unique_for_overwrite<T[]>(array-size)
std::unique_ptr
auto createSequence(size_t length) {
    auto seq = std::make_unique_for_overwrite<int[]>(length);
    seq[0] = seq[1] = 1;
    for (int i = 2; i < length; ++i) {
        seq[i] = seq[i - 2] + seq[i - 1];
    }
    return seq;
};
 auto fopenUnique(const char* filename, const char* flags) {
   return std::unique_ptr<std::FILE, int(*)(std::FILE*)>{
       std::fopen(filename, flags), &std::fclose};
 }
int* ar = new int[10];
// adopt existing resource
auto unique_ar = std::unique_ptr<int[]>{ar};
// construct new resource
auto my_p = std::make_unique<int[]>(5);
// release ownership and aquire new resource
unique_ar = some_ptr;
// steal ownership
unique_ar = std::move(my_p);
std::shared_ptr
shared_ptr

resource

use_count:
1

Reference counting

std::shared_ptr
shared_ptr

resource

use_count:
1
weak_ptr

Reference counting

std::shared_ptr
shared_ptr

resource

use_count:
1
weak_ptr
weak_ptr

Reference counting

std::shared_ptr
shared_ptr

resource

use_count:
2
weak_ptr
weak_ptr
shared_ptr

Reference counting

std::shared_ptr
shared_ptr

resource

use_count:
2
weak_ptr
shared_ptr

Reference counting

std::shared_ptr
shared_ptr

resource

use_count:
1
weak_ptr

Reference counting

std::shared_ptr
weak_ptr

Reference counting

use_count:
0
std::shared_ptr

Reference counting

resource

use_count
shared_ptr
shared_ptr
shared_ptr
std::shared_ptr

Reference counting

resource

use_count
shared_ptr
shared_ptr
std::shared_ptr

Reference counting

resource

use_count
shared_ptr(new Object{args})

resource

use_count
make_shared<Object>(args)
std::shared_ptr

Reference counting

resource

use_count
Resource* ptr = new Resource{};

std::shared_ptr smart1{ptr};
std::shared_ptr smart2{smart1};

std::shared_ptr smart3{ptr};
std::shared_ptr smart4{smart1};
use_count
smart1
smart2
smart3
smart4
std::shared_ptr
std::shared_ptr<T>

std::shared_ptr{new-allocated-pointer}
std::shared_ptr<T, DeleterT>{pointer, deleter}
std::shared_ptr<T>{shared-ptr}
std::shared_ptr<T>{weak-ptr}
std::shared_ptr<T>{unique-ptr}
std::shared_ptr<T>{shared-ptr, pointer}
std::make_shared<T>(initializer)
std::make_shared<T[]>(array-size)
std::make_shared_for_overwrite<T>()
std::make_shared_for_overwrite<T[]>(array-size)

weak_ptr.lock()
std::enable_shared_from_this

shared_ptr.use_count()

aliasing constructor

std::shared_ptr
std::unique_ptr<int[]> createSequence(size_t length);
// steal from unique_ptr
std::shared_ptr<int[]> shared = createSequence(10);
auto shared1 = std::make_shared<std::string>("Hello");
auto shared2 = shared1; // copy ownership
std::weak_ptr weak = shared1; // only access
// (1)
auto reader1 = weak.lock(); // read through weak_ptr
if (!reader1) // weak_ptr could've been expired
    std::cout << "expired";
else
    somePointerUser(*reader1);
// (2)
std::shared_ptr reader2{weak}; // throws if expired
somePointerUser(*reader2);

assert(shared1.use_count() == 4);
struct point { int x, y; };
auto point_ptr = std::make_shared<point>(3, 5);
auto coord_ptr = std::shared_ptr<int>(point_ptr, &point_ptr->x);
std::weak_ptr

resource

resource

resource

resource

resource

resource

std::weak_ptr

resource

resource

resource

resource

resource

resource

std::weak_ptr

resource

resource

resource

resource

resource

resource

pointers

  • Storing values
    1. Unique ownership
      1. Automatic variables
      2. std::unique_ptr
      3. raw pointer/handle + RAII wrapper
    2. Shared ownership
      • std::shared_pointer
      • std::weak_ptr
  • Taking parameters
    1. By value or reference
    2. By owning class (like smart pointer)
    3. By raw pointer
  • Returning values
    1. By value or reference
    2. By owning class (like smart pointer)
    3. By raw pointer

inheritance

Has-a and Is-a object RELATIONSHIP

Has-A

is-a

computer

  • Motherboard
  • Processor
  • Memory
  • Storage
  • Graphics Card

computer

  • Turing machine
  • Electronic device
  • Hardware

Has-A

is-a

Has-a RELATIONSHIP

Has-A

struct Person {
    int age;
    std::string name;
    Job occupation;
};

person

  • age
  • name
  • occupation
class Array {
private:
    int* data; // raw pointer encapsulated
    size_t capacity;
public:
    int& operator[](size_t idx);
    const int& operator[](size_t idx) const;
    ...
};

array

  • array elements
  • size

Is-a RELATIONSHIP

is-a

inheritance

base

derived

Is-a RELATIONSHIP

is-a

inheritance

base

derived

  • base members
  • base methods
  • derived members
  • derived methods

base

  • base members
  • base methods

Is-a RELATIONSHIP

is-a

inheritance

base

derived

  • base members
  • base methods
  • derived members
  • derived methods

base

  • base members
  • base methods

Is-a RELATIONSHIP

is-a

inheritance

shape

rectangle

  • position
  • draw()
  • width
  • height

Shape

  • position
  • draw()

inheritance

public

inheritance

private

inheritance

protected

inheritance

Has-A

Has-A

is-a

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {

};
class Derived : public Base { 

};
Derived
Base

is-a

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {
    int x, y;
};
class Derived : public Base { 
    int z, w;
};

memory location

object

derived

z
Base
w

Base

x
y

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {
    int x, y;
};
class Derived : public Base { 
    int z, w;
};

memory location

object

derived

z
Base
w

Base

x
y

Empty Base Optimisation if

sizeof(Base) == 0

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {
    int x, y;
};
class Derived : public Base { 
    int z, w;
};
Derived derived;
Base* pBase = &derived;
Base& refBase = derived;

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {
public:
    void methodOnBase() {
        std::cout << "base\n";
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << "derived\n";
    }
};
Base base;
base.methodOnBase();

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();

public inheritance

public

inheritance

is-a

Is-A RELATIONSHIP

class Base {
public:
    int data;
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;

protected access

class Base {
public:
    int data;
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;
class Base {
    int data;
public:
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;

protected access

class Base {
protected:
    int data;
public:
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;

protected access

class Base {
protected:
    int data;
public:
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;

using declaration

class Base {
protected:
    int data;
public:
    void methodOnBase() {
        std::cout << data;
    }
};
class Derived : public Base { 
public:
    using Base::data;
    void methodOnDerived() {
        std::cout << data + 143;
    }
};
Base base;
base.methodOnBase();
base.data = 245;

Derived derived;
derived.methodOnDerived();
derived.methodOnBase();
derived.data = 1252;

using declaration

class Base {
public:
    void f() {
        std::cout << "base";
    };
};
class Derived : public Base { 
};
Base base;
base.f(); // base

Derived derived;
derived.f(); // base

shadowing

class Base {
public:
    void f() {
        std::cout << "base";
    };
};
class Derived : public Base {
public:
    void f() {
        std::cout << "derived";
    };
};
Base base;
base.f(); // base

Derived derived;
derived.f(); // derived

shadowing

class Base {
public:
    void f() {
        std::cout << "base";
    };
};
class Derived : public Base {
public:
    void f(int x) {
        std::cout << x;
    };
};
Base base;
base.f(); // base

Derived derived;
derived.f(); // ERROR
derived.f(15); // 15

shadowing

class Base {
public:
    void f() {
        std::cout << "base";
    };
};
class Derived : public Base {
public:
    using Base::f;
    void f(int x) {
        std::cout << x;
    };
};
Base base;
base.f(); // base

Derived derived;
derived.f(); // ERROR
derived.f(15); // 15

shadowing

public inheritance

dog

terrier

shows relationship

code reuse

polymorphism

class Dog {
    void bark();
};

class Terrier : public Dog {
    void hunt();
};
class Dog {
    virtual void bark();
};

class Terrier : public Dog {
    void hunt() override;
};

type erasure, CRTP...

POLYMORPHISM

class Dog {
public:
    void bark() {
        std::cout << "I'm a generic dog\n";
    }
};

class Terrier : public Dog {
public:
    void bark() {
        std::cout << "I'm a terrier\n";
    };
};
std::unique_ptr<Dog> dog = std::make_unique<Terrier>();
dog->bark();

POLYMORPHISM

class Dog {
public:
    virtual void bark() {
        std::cout << "I'm a generic dog\n";
    }
    virtual ~Dog() = default;
};

class Terrier : public Dog {
public:
    void bark() override {
        std::cout << "I'm a terrier\n";
    };
};
std::unique_ptr<Dog> dog = std::make_unique<Terrier>();
dog->bark();

smart pointers

and inheritance

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

  • protected members

  • using declaration

  • final specifier

  • private & protected inheritance

  • multiple inheritance

  • diamond problem

  • empty base optimization

  • base method hiding problem (shadowing)

  • virtual methods

  • override specifier

  • dynamic type

    • RTTI

    • dynamic_cast

    • typeid

    • vtables

  • by value passing problem (slicing)

  • pure virtual function

  • abstract class

misc

  • friends

  • nested members

  • conversion operators

  • operator overloading

  • member pointers

  • unions

    • std optional

    • std variant

    • std any

type ereasure

pimpl

enable shared from this with private inheritance

linkage and static (non-member) functions

Smart Pointers and Inheritance

By Jan Bielak

Smart Pointers and Inheritance

A presentation about different ways of taking function parameters and returning values from functions and smart pointers such as std::unique_ptr, std::shared_ptr, std::weak_ptr as well as public inheritance and the protected qualifier, in-class using-declarations, base class member shadowing and the basics of polymorphism. It is presented here: https://www.youtube.com/watch?v=pIj3C6sveUM .

  • 327