Parent directory Makefile inherit1.cpp inherit2.cpp inherit3.cpp template1.cpp template2.cpp template3.cpp
Download
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 }