Parent directory
Makefile
inherit1.cpp
inherit2.cpp
inherit3.cpp
template1.cpp
template2.cpp
template3.cpp
CC = g++
CXX = g++
CFLAGS = -g -Wall
CXXFLAGS = -g -Wall -std=c++14 -fno-elide-constructors
executables = template1 template2 template3
.PHONY: default
default: $(executables)
template1: template1.o
template2: template2.o
template3: template3.o
.PHONY: clean
clean:
rm -f *~ a.out core *.o $(executables)
.PHONY: all
all: clean default
#include <iostream>
using namespace std;
#define w(expr) cout << #expr << ": " << expr << endl;
class B {
public:
virtual double sum() const { return b; }
//protected:
private:
double b = 100.0;
};
class D : public B {
public:
//double sum() const { return b + d; }
double sum() const override { return B::sum() + d; }
private:
double d = 200.0;
};
int main()
{
D x;
w( x.sum() )
cout << "*** Object layout" << endl;
w( sizeof(x) )
double *p = (double *)&x;
w( p[0] )
w( p[1] )
cout << "*** Pointer/reference to B can bind to a D object" << endl;
B *pb = &x;
D *pd = &x;
w( pb )
w( pd )
w( pb->sum() ) // static vs. dynamic binding
w( pd->sum() )
cout << "*** Calling virtual function manually" << endl;
cout << ((double (***)(const D*))pb)[0][0](&x) << endl;
}
#include <iostream>
using namespace std;
#define w(expr) cout << #expr << ": " << expr << endl;
class B {
public:
double sum() const { return b; }
private:
double b = 100.0;
};
class C {
public:
double sum() const { return c; }
private:
double c = 150.0;
};
class D : public B, public C {
public:
double sum() const { return B::sum() + C::sum() + d; }
private:
double d = 200.0;
};
int main()
{
D x;
w( x.sum() )
cout << "*** Object layout" << endl;
w( sizeof(x) )
double *p = (double *)&x;
w( p[0] )
w( p[1] )
w( p[2] )
cout << "*** Base pointer may have a different address" << endl;
B *pb = &x;
C *pc = &x;
D *pd = &x;
w( pb )
w( pc )
w( pd )
w( pb->sum() )
w( pc->sum() )
w( pd->sum() )
}
#include <iostream>
using namespace std;
#define w(expr) cout << #expr << ": " << expr << endl;
struct S1 : public string {
S1(const char *s = "") : string{s} { cout << "S1::S1()" << endl; }
~S1() { cout << "S1::~S1()" << endl; }
};
struct S2 : public string {
S2(const char *s = "") : string{s} { cout << "S2::S2()" << endl; }
~S2() { cout << "S2::~S2()" << endl; }
};
class B {
public:
B() { cout << "B::B()" << endl; }
B(double x) : b{x} { cout << "B::B(double)" << endl; }
~B() { cout << "B::~B()" << endl; }
double sum() const { return b; }
private:
double b = 100.0;
};
class C {
public:
C() { cout << "C::C()" << endl; }
C(double x) : c{x} { cout << "C::C(double)" << endl; }
~C() { cout << "C::~C()" << endl; }
double sum() const { return c; }
private:
double c = 150.0;
};
class D : public B, public C {
public:
D() : s2{"xyz"}, s1{"abc"}, C{149}, B{99} {
cout << "D::D()" << endl;
}
~D() {
cout << "D::~D()" << endl;
}
double sum() const { return B::sum() + C::sum() + d; }
private:
double d = 200.0;
S1 s1;
S2 s2;
};
int main()
{
D x;
w( x.sum() )
w( sizeof(x) )
}
#include <string>
#include <iostream>
/*
int Max(int x, int y)
{
return x > y ? x : y;
}
std::string Max(std::string x, std::string y)
{
return x > y ? x : y;
}
*/
template <typename T>
const T& Max(const T& x, const T& y)
{
return x > y ? x : y;
}
std::string Max(const char *x, const char *y)
{
std::string s1(x);
std::string s2(y);
return s1 > s2 ? s1 : s2;
}
int main()
{
using namespace std;
cout << Max(3, 4) << endl;
cout << Max(string{"abc"}, string{"xyz"}) << endl;
// calls Max(const char *, const char *) that we wrote separately
cout << Max("abc", "xyz") << endl;
}
#include <string>
#include <iostream>
class IntArray {
public:
IntArray() : sz{0}, cap{1}, a{new int[cap]} {}
~IntArray() {
delete[] a;
}
IntArray(const IntArray&) = delete;
IntArray& operator=(const IntArray&) = delete;
// IntArray(const IntArray&) = default;
// IntArray& operator=(const IntArray&) = default;
IntArray(IntArray&& ia) : sz{ia.sz}, cap{ia.cap}, a{ia.a} {
ia.sz = ia.cap = 0;
ia.a = nullptr;
std::cout << "move ctor" << std::endl;
}
IntArray& operator=(IntArray&& ia) {
if (this != &ia) {
delete[] a;
sz = ia.sz;
cap = ia.cap;
a = ia.a;
ia.sz = ia.cap = 0;
ia.a = nullptr;
}
std::cout << "move assignment" << std::endl;
return *this;
}
int& operator[](int i) { return a[i]; }
const int& operator[](int i) const { return a[i]; }
int size() const { return sz; }
int capacity() const { return cap; }
void push_back(int x) {
if (sz == cap) {
// Separate cap*=2 to preserve invariant on exception
// int *a2 = new int[cap *= 2];
int *a2 = new int[cap * 2];
cap *= 2;
std::copy(a, a+sz, a2);
delete[] a;
a = a2;
}
a[sz++] = x;
}
private:
size_t sz;
size_t cap;
int *a;
};
std::ostream& operator<<(std::ostream& os, const IntArray& ia)
{
for (int i = 0; i < ia.size(); i++) {
os << ia[i] << " ";
}
std::cout << "(cap=" << ia.capacity() << ")" << std::flush;
return os;
}
using namespace std;
IntArray createIntArray()
{
IntArray ia;
for (int i = 0; i < 20; i++) {
ia.push_back(i);
cout << ia << endl;
}
return ia;
}
int main()
{
IntArray ia { createIntArray() };
IntArray ia2;
//ia2 = (IntArray&&)ia;
ia2 = std::move(ia);
// Lecture plan:
//
// 1. IntArray code walk-through
//
// - ctor initializer syntax
// - =delete & =default
// - invariants
//
// 2. move ctor
//
// - rvalue reference
// - -fno-elide-constructors
//
// 3. move assignment
//
// - casting to rvalue reference
//
// 4. IntArray to Vec<T>
//
// - Vec<int>
// - Vec<string>
//
// 5. vector<string>
//
// - range for loop
}
#include <string>
#include <iostream>
template <typename T>
class Vec {
public:
Vec() : sz{0}, cap{1}, a{new T[cap]} {}
~Vec() {
delete[] a;
}
Vec(const Vec&) = delete;
Vec& operator=(const Vec&) = delete;
// Vec(const Vec&) = default;
// Vec& operator=(const Vec&) = default;
Vec(Vec&& ia) : sz{ia.sz}, cap{ia.cap}, a{ia.a} {
ia.sz = ia.cap = 0;
ia.a = nullptr;
std::cout << "move ctor" << std::endl;
}
Vec& operator=(Vec&& ia) {
if (this != &ia) {
delete[] a;
sz = ia.sz;
cap = ia.cap;
a = ia.a;
ia.sz = ia.cap = 0;
ia.a = nullptr;
}
std::cout << "move assignment" << std::endl;
return *this;
}
T& operator[](int i) { return a[i]; }
const T& operator[](int i) const { return a[i]; }
int size() const { return sz; }
int capacity() const { return cap; }
void push_back(T x) {
if (sz == cap) {
// Separate cap*=2 to preserve invariant on exception
// T *a2 = new T[cap *= 2];
T *a2 = new T[cap * 2];
cap *= 2;
std::copy(a, a+sz, a2);
delete[] a;
a = a2;
}
a[sz++] = x;
}
private:
size_t sz;
size_t cap;
T *a;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Vec<T>& ia)
{
for (int i = 0; i < ia.size(); i++) {
os << ia[i] << " ";
}
std::cout << "(cap=" << ia.capacity() << ")" << std::flush;
return os;
}
using namespace std;
Vec<int> createVecInt()
{
Vec<int> ia;
for (int i = 0; i < 20; i++) {
ia.push_back(i);
cout << ia << endl;
}
return ia;
}
Vec<string> createVecStr()
{
Vec<string> ia;
for (char c = 'A'; c <= 'Z'; c++) {
string s;
s += c;
ia.push_back(s);
cout << ia << endl;
}
return ia;
}
int main()
{
Vec<int> ia { createVecInt() };
Vec<string> ia2 { createVecStr() };
// Lecture plan:
//
// 1. IntArray code walk-through
//
// - ctor initializer syntax
// - =delete & =default
// - invariants
//
// 2. move ctor
//
// - rvalue reference
// - -fno-elide-constructors
//
// 3. move assignment
//
// - casting to rvalue reference
//
// 4. IntArray to Vec<T>
//
// - Vec<int>
// - Vec<string>
//
// 5. vector<string>
//
// - range for loop
}