COMS W4995 C++ for C Programmers

Index of 2023-5/code/0622

Parent directory
Makefile
c2cpp_util.h
smartptr.cpp

Makefile

CC  = g++
CXX = g++

CFLAGS   = -Wall -g
CXXFLAGS = -Wall -g -std=c++17

# turn off warnings about unused variables
CXXFLAGS += -Wno-unused-variable

executables = smartptr

.PHONY: default
default: $(executables)

.PHONY: clean
clean:
	rm -f a.out core *.o $(executables)

.PHONY: all
all: clean default

c2cpp_util.h

#include <cxxabi.h>
#include <typeinfo>
#include <cassert>
#include <cstdlib>

#ifdef __GNUC__
// use abi::__cxa_demangle to demangle type name in g++ & clang++
#define PRINT_TYPE(os, x) do {                                      \
    int status;                                                     \
    char *s = abi::__cxa_demangle(typeid(x).name(), 0, 0, &status); \
    assert(status == 0);                                            \
    os << s << '\n';                                                \
    std::free(s);                                                   \
} while (0)
#else
// MSVC returns human-readable type name, according to cppreference.com
#define PRINT_TYPE(os, x) do {                                      \
    os << typeid(x).name() << '\n';                                 \
} while (0)
#endif

smartptr.cpp

#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <utility>
#include <memory>
#include "c2cpp_util.h"
using namespace std;

template <class T>
class SmartPtr {
    T*   ptr;    // the underlying pointer
    int* count;  // the reference count
public:
    explicit SmartPtr(T *p = 0) : ptr{p}, count{new int(1)}  { }
    ~SmartPtr() {
        if (--*count == 0) {
            delete count;
            delete ptr;
        }
    }

    SmartPtr(const SmartPtr<T>& sp) : ptr(sp.ptr), count(sp.count) {
        ++*count; 
    }
    SmartPtr<T>& operator=(const SmartPtr<T>& sp) {
        if (this != &sp) {
            // first, detach.
            if (--*count == 0) {
                delete count;
                delete ptr;
            }
            // attach to the new object.
            ptr = sp.ptr;
            count = sp.count;
            ++*count;
        }
        return *this;
    }

    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }

    T* getPtr() const { return ptr; } // access to underlying ptr
    operator void*() const { return ptr; } // enables "if (sp) ..."
};

auto create_vec()
{
    SmartPtr<string> p1 { new string("hello") };
    SmartPtr<string> p2 { new string("c") };
    SmartPtr<string> p3 { new string("students") };

    vector v { p1, p2, p3 };
    return v;
}

int main()
{
    vector v1 = create_vec();
    for (auto&& p : v1) { cout << *p + " "; } cout << '\n';

    vector v2 = v1;
    *v2[1] = "c2cpp";
    *v2[2] = "hackers"; 

    // print v1 again
    for (auto&& p : v1) { cout << *p + " "; } cout << '\n';
}

/* Lecture outline:

1. SmartPtr

2. MakeSmartPtr

    template <typename T, typename... Args>
    SmartPtr<T> MakeSmartPtr(Args&&... args)
    {
        return SmartPtr<T>(new T(std::forward<Args>(args)...));
    }

    auto create_vec()
    {
        auto p1 { MakeSmartPtr<string>("hello") };
        auto p2 { MakeSmartPtr<string>("c") };
        auto p3 { MakeSmartPtr<string>("students") };

        vector v { p1, p2, p3 };
        return v;
    }

3. make_shared

    auto create_vec()
    {
        // make_shared<T>() creates shared_ptr<T>

        auto p1 { make_shared<string>("hello") };
        auto p2 { make_shared<string>("c") };
        auto p3 { make_shared<string>("students") };

        static_assert(is_same_v<decltype(p3), shared_ptr<string>>);

        vector v { p1, p2, p3 };
        return v;
    }

4. make_shared() combines allocation of T and control block

    shared_ptr<string> p { new string("hello") };
    cout << *p << '\n';

5. shared_ptr to an array and custom deleter

    SmartPtr<string> p { new string[2] {"hello", "c2cpp"} };
    shared_ptr<string> p { new string[2] {"hello", "c2cpp"} };
    shared_ptr<string> p { new string[2] {"hello", "c2cpp"},
        [] (string* s) { delete[] s; }
    };
    shared_ptr<string[]> p { new string[2] {"hello", "c2cpp"} };
    cout << p[0] << " " << p[1] << '\n';

6. aliasing

    shared_ptr<pair<string,int>> p1 { new pair{"hello"s, 4995} };
    shared_ptr<int>              p2 { p1, &p1->second };
    cout << p1->first << " " << *p2 << '\n';
    cout << p1.use_count() << '\n';
    cout << p2.use_count() << '\n';

7. shared_ptr has many more features

    - works with unique_ptr:

        template< class Y, class Deleter >
        shared_ptr( std::unique_ptr<Y, Deleter>&& r );

    - can attach weak_ptr, which does not participate in ref counting
    - thread-safe
    - and more... 

*/