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
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
- 684