Parent directory Makefile c2cpp_util.h smartptr.cpp
Download
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
#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
#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... */