COMS W4995 C++ for C Programmers

Index of 2023-5/code/0601

Parent directory
Makefile
inherit1.cpp
inherit2.cpp
inherit3.cpp
template1.cpp
template2.cpp
template3.cpp

Makefile

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

inherit1.cpp

#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;
}

inherit2.cpp

#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() )
}

inherit3.cpp

#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) )
}

template1.cpp

#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;
}

template2.cpp

#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
}

template3.cpp

#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
}