OOp in C++

Part IX

union
union in_or_float {
    int i;
    float f;
};
struct in_and_float {
    int i;
    float f;
};
int_or_float dat;
dat.i = 3;
dat.f = 2.0f;

std::cout << f; // 2.0f
std::cout << i; // UB!
int_and_float dat;
dat.i = 3;
dat.f = 2.0f;

std::cout << f; // 2.0f
std::cout << i; // 3
union name {
    member1_t member1;
    member2_t member2;
    member3_t member3;
	/* ... */
    memberN_t memberN;
};
alignof(name) == max(alignof(member1_t), alignof(member2_t), ...)
sizeof(name) == max(sizeof(member1_t), sizeof(member2_t), ...)
union
union u {
    t1 member1;
    t2 member2;
    t3 member3;
	/* ... */
    tN memberN;
};
struct s {
    t1 member1;
    t2 member2;
    t3 member3;
	/* ... */
    tN memberN;
};

sum type

product type

\mathbb{S}_{\tt{T}}-\text{ the set of possible states of an object of type \tt{T}}
\mathbb{S}_{\tt{u}}=\mathbb{S}_{\tt{t1}}+\mathbb{S}_{\tt{t2}}+...+\mathbb{S}_{\tt{tN}}
\mathbb{S}_{\tt{s}}=\mathbb{S}_{\tt{t1}}\times\mathbb{S}_{\tt{t2}}\times...\times\mathbb{S}_{\tt{tN}}
union
union
std::variant<t1, t2, t3, /*...*/ tN>
union u {
    t1 member1;
    t2 member2;
    t3 member3;
	/* ... */
    tN memberN;
};
std::bit_cast<t2>(t1{})
union
struct vec4 {
    union { float x, r, u; };
    union { float y, g, v; };
    union { float z, b, p; };
    union { float w, a, q; };
};
vec4 tex_coords{};
tex_coords.u = 0.5;
tex_coords.v = 0.9;
vec4 position{};
position.x = 13.0;
position.y = -5.0;
position.z = 4.0;
position.w = 1.0;
vec4 color{};
color.r = 0.7;
color.g = 0.2;
color.b = 0.3;
std::cout << color.x;
// 0.7
std::cout << position.a;
// 1.0
std::cout << tex_coords.z;
// 0.0

aggregate initialization

Aggregate:

  • array
  • class with:
    • no direct non-public non-static data members
    • no user-declared or inherited constructors
    • only public non-virtual base classes
    • no virtual functions
int ar[] {3, 1, 4, 1, 5, 9};
std::string s[]{"The", "quick", "brown", "fox"};
namespace vk {
  struct ApplicationInfo {
      vk::StructureType sType
          = StructureType::eApplicationInfo;
      const void* pNext{};
      const char* pApplicationName{};
      uint32_t applicationVersion{};
      const char* pEngineName{};
      uint32_t engineVersion{};
      uint32_t apiVersion{};
  };
}
vk::ApplicationInfo applicationInfo{ 
    .pApplicationName   = "Space Race Ultimate",
    .applicationVersion = 1,
    .pEngineName        = "Real Engine",
    .engineVersion      = 1,
    .apiVersion         = VK_API_VERSION_1_1
};
struct point {
    float x, y, z;
}
point ball_position {
    2.0, -5.0, 1.0
};
#include <type_traits>
std::is_aggregate_v<T>

protected and private inheritance

protected

private

Base Derived
public public
protected protected
private -
Base Derived
public protected
protected protected
private -
Base Derived
public private
protected private
private -
public

inheritance

private

inheritance

protected

inheritance

Has-A

Has-A

is-a

class Derived : public Base {
    ...
};
class Derived : protected Base {
    ...
};
class Derived : private Base {
    ...
};

protected and private inheritance

protected

private

public

inheritance

private

inheritance

protected

inheritance

Has-A

Has-A

is-a

inherits interface

inherits implementation

(hidden from subclasses)

inherits implementation

(visible to subclasses)

protected and private inheritance

protected

private

inherit part of interface

class stack : std::vector<int> {
public:
    using std::vector<int>::push_back;
    using std::vector<int>::emplace_back;
    using std::vector<int>::pop_back;
    using std::vector<int>::back;
    using std::vector<int>::empty;
    using std::vector<int>::size;
};
class stack {
private:
    std::vector<int> data;
    using reference = std::vector<int>::reference;
    using const_reference = std::vector<int>::const_reference;
public:
    void push(int val) { data.push_back(val); }
    template <typename... Args>
    reference emplace(Args&&... args) {
        return data.emplace_back(std::forward<Args>(args)...);
    }
    void pop() noexcept { data.pop_back(); }
    [[nodiscard]] reference top() noexcept { return data.back(); }
    [[nodiscard]] const_reference top() const noexcept { return data.back(); }
    [[nodiscard]] bool empty() const noexcept { return data.empty(); }
    [[nodiscard]] size_t size() const noexcept { return data.size(); }
};

protected and private inheritance

protected

private

inherit part of interface

class stack : std::vector<int> {
public:
    using std::vector<int>::push_back;
    using std::vector<int>::emplace_back;
    using std::vector<int>::pop_back;
    using std::vector<int>::back;
    using std::vector<int>::empty;
    using std::vector<int>::size;
};
class stack {
private:
    std::vector<int> data;
    using reference = std::vector<int>::reference;
    using const_reference = std::vector<int>::const_reference;
public:
    void push(int val) { data.push_back(val); }
    template <typename... Args>
    reference emplace(Args&&... args) {
        return data.emplace_back(std::forward<Args>(args)...);
    }
    void pop() noexcept { data.pop_back(); }
    [[nodiscard]] reference top() noexcept { return data.back(); }
    [[nodiscard]] const_reference top() const noexcept { return data.back(); }
    [[nodiscard]] bool empty() const noexcept { return data.empty(); }
    [[nodiscard]] size_t size() const noexcept { return data.size(); }
};

protected and private inheritance

protected

private

prevent upcasting

struct Move {
    ~Move() {
        std::cout << "end"; // not called
    }
};
struct Dog {
    std::string bark_sound;
    void bark() const {
        std::cout << bark_sound << '\n';
    }
};
struct DancingDog : Dog {
    std::vector<Move> dance_moves{Move{}};
    void dance() const {
        ExitWindowsEx(EWX_POWEROFF|EWX_FORCE, 0);
    }
};
{
    std::unique_ptr<Dog> pDog(new DancingDog());
} // Dog::~Dog rather than DancingDog::~DancingDog

protected and private inheritance

protected

private

prevent upcasting

struct Move {
    ~Move() {
        std::cout << "end"; // not called
    }
};
struct Dog {
    std::string bark_sound;
    void bark() const {
        std::cout << bark_sound << '\n';
    }
};
struct DancingDog : private Dog {
    using Dog::bark;
    using Dog::bark_sound;
    std::vector<Move> dance_moves{Move{}};
    void dance() const {
        ExitWindowsEx(EWX_POWEROFF|EWX_FORCE, 0);
    }
};
{
    std::unique_ptr<Dog> pDog(new DancingDog());
    // compile error
}

ref-QUALIFICATION

struct loud {
    loud()            { std::cout << "default ctor\n"; };
    loud(const loud&) { std::cout << "copy ctor\n"; };
    loud(loud&&)      { std::cout << "move ctor\n"; };
    ~loud()           { std::cout << "dtor\n"; };
};
class container {
    loud data;
public:
    loud& get() { return data; }
    const loud& get() const { return data; }
};
container c;
// default ctor
loud& l = c.get();
// -- nothing --
const container cc;
// default ctor
loud& l1 = c.get();
// !! error above !!
const loud& l2 = c.get();
// -- nothing --

ref-QUALIFICATION

struct loud {
    loud()            { std::cout << "default ctor\n"; };
    loud(const loud&) { std::cout << "copy ctor\n"; };
    loud(loud&&)      { std::cout << "move ctor\n"; };
    ~loud()           { std::cout << "dtor\n"; };
};
class container {
    loud data;
public:
    loud& get() { return data; }
    const loud& get() const { return data; }
};
container c;
// default ctor
loud& l = c.get();
// -- nothing --
const container cc;
// default ctor
loud& l1 = c.get();
// !! error above !!
const loud& l2 = c.get();
// -- nothing --

loud L = container{}.get();
// default ctor
// copy ctor
// dtor

ref-QUALIFICATION

struct loud {
    loud()            { std::cout << "default ctor\n"; };
    loud(const loud&) { std::cout << "copy ctor\n"; };
    loud(loud&&)      { std::cout << "move ctor\n"; };
    ~loud()           { std::cout << "dtor\n"; };
};
class container {
    loud data;
public:
    loud& get() & { return data; }
    const loud& get() const& { return data; }
    loud&& get() && { return std::move(data); }
};
container c;
// default ctor
loud& l = c.get();
// -- nothing --
const container cc;
// default ctor
loud& l1 = c.get();
// !! error above !!
const loud& l2 = c.get();
// -- nothing --

loud L = container{}.get();
// default ctor
// move ctor
// dtor

IMPLEMENTING classes in source files

classes

source files

class C {
private:
    inline static std::string s_name = "C";
    int m_data;
public:
    /* inline */
    double member_func(int x, char c) const {
        return x * static_cast<float>(c) / 13.0f;
    }
};

header file

IMPLEMENTING classes in source files

classes

source files

header file

class C {
private:
    static std::string s_name;
    int m_data;
public:
    double member_func(int x, char c) const;
};
std::string C::s_name = "C";

double C::member_func(int x, char c) const {
    return x * static_cast<float>(c) / 13.0f;
}

source file

pimpl

header file

class widget {
    class impl;
    std::experimental::propagate_const<std::unique_ptr<impl>> pImpl;
 public:
    void draw() const;
    void draw();
    bool shown() const { return true; }
    widget(int);
    ~widget();
    widget(widget&&);
    widget(const widget&) = delete;
    widget& operator=(widget&&);
    widget& operator=(const widget&) = delete;
};
class widget::impl {
    int n;
 public:
    void draw(const widget& w) const {
        if(w.shown())
            std::cout << "drawing a const widget " << n << '\n';
    }
    void draw(const widget& w) {
        if(w.shown())
            std::cout << "drawing a non-const widget " << n << '\n';
    }
    impl(int n) : n(n) {}
};
void widget::draw() const { pImpl->draw(*this); }
void widget::draw() { pImpl->draw(*this); }
widget::widget(int n) : pImpl{std::make_unique<impl>(n)} {}
widget::widget(widget&&) = default;
widget::~widget() = default;
widget& widget::operator=(widget&&) = default;

source file

OOp in C++

Part IX

PLAN

  • Theory
    • dynamic_cast, downcast, upcast, sidecast, std::bad_cast
    • union
    • private & protected inheritance
    • constexpr virtual
    • aggregate, pod, literal type, aggregate initization
    • function member ref qualification
    • Liskov substitution principle
  • Techniques
    • implement class in cpp file
    • pimpl
    • delegation to sister class
    • enable shared from this with private 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

linkage and static (non-member) functions

OOP Part 9

By Jan Bielak

OOP Part 9

  • 701