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