8. OOP: Smart Pointer

Smart Pointer in C++

0. Overview

We have touched the pointer in C++ previously in Linked List part. This time, we’re going to handle the smart pointer in C++. Before dig into the detail, let’s have a comprehensive understanding of that.

We need to admit it that pointer can be a tricky to newbie, especially the de-allocation of pointer. When we forget the delete the pointer we created after using, it could lead to memory leak. Thus, smart pointer is here.

First of all, let’s recall that when we “delete ptr”, we delete the object pointed by ptr, instead the pointer itself. Thus,the principle of the smart pointer is to automatically delete the object that have no pointer point to it. To be more clear, for an object, if there is pointer pointed to it, then this object will be kept because some pointer need it; When there is no pointer point to it, it means this object is useless, which need to be delete to free the memory.

There are three types of smart pointer: unique_ptr, shared_ptr, weak_ptr.

As mentioned before, when there exists pointer point to the object, the object will be kept. But how to check this? We count the number of pointers that point to an object. This makes the difference among three types of smart pointers.

For unique_ptr, this object can only be pointed by this unique_ptr, and the count will and only will be one.
For shared_ptr, it’s the most frequent used type. The object’s count is the number of sharted_ptr pointed to it.
For weak_ptr, as its name, it cannot count as a pointer, but it exists. We could take this as a direction, we could use weak_ptr to track something, but we cannot count on weak_ptr to help us keep the object; Only unique_ptr and shared_ptr could. However, when we want weak_ptr to get the information of the object, we could use function weak_ptr->lock() to cast type from weak_ptr to shared_ptr.

Well, so far we have the overall understanding of the smart pointer. For more detail, such as how to create smart pointer, how to use, and how it facilitate the code, explore the code then.

1. Smart Pointers


//Smart pointers
#include <iostream>
#include <memory> //smart pointers
using namespace std;

class ThreeD {
public:
    int ht;
    int wid;
    int dep;
    ThreeD() { ht = wid = dep = 0; }
    ThreeD(int i, int j, int k) { ht = i; wid = j; dep = k; }
};

class node {
public:
    /*
    node * next;
    int value;
    node() { next = nullptr; }
    node(int i) { next = nullptr; value = i; }
    */
    shared_ptr<node> next;
    int value;
    node() {}//all smart pointers will have initial default value nullptr
    node(int i) { value = i; }
};
class linked_list {
public:
    //node * head;
    shared_ptr<node> head;
    //linked_list() { head = nullptr; }
    linked_list() {}
    linked_list(const initializer_list<int>& V) {
        const int * p = V.end() - 1;
        while (p != V.begin() - 1) {
            //node * p1 = new node(*it1);
            shared_ptr<node> p1 = make_shared<node>(*p);
            p1->next = head;
            head = p1;
            p--;
        }
    }
};
// ==========Function frequently uesed in smart pointer===============
// unique_ptr<T> up = make_unique<T>(10);
// up.reset() // clear the pointer of up
// shared_ptr<T> sp = make_shared<T>(10);
// weak_ptr<T> wp = sp;
// sp.use_count()
// wp.lock()
// wp.expired()
int main() {
    //Raw pointers refer to the original pointers
    //Three types of smart pointers: unique_ptr, shared_ptr, weak_ptr
    unique_ptr<int> up1 = make_unique<int>(10);
    up1.reset();// up1 becomes empty; object 10's ref count = 0 and will be deleted automatically.
    //up1= nullptr;

    up1 = make_unique<int>(15);
    cout << *up1 << endl;//15
    //unique_prt<int> up2 = up1; error! because the unique object can only be pointed by
    //one unique pointer

    shared_ptr<int> sp1 = make_shared<int>(20); //object 20's reference count is 1
    shared_ptr<int> sp2 = sp1; //reference count is 2
    shared_ptr<int> sp3 = make_shared<int>(30);
    sp2 = sp3; //ref count for object 20 is 1
    sp1 = sp3; //ref count for object 20 is 0 and the object 20 will be automatically deleted.
    
    cout << sp1.use_count() << endl;//use_count returns reference count of the object pointed by sp1
    shared_ptr<int> sp4;
    weak_ptr<int> wp1;
    wp1 = sp3; //ref count for object 30 remains 3; weak_ptr does not contribute to ref count
               //sp4 = wp1; error! can't assign weak_ptr to shared_ptr
    cout << sp3.use_count() << endl;//3


    sp4 = wp1.lock();//lock() type "casting" weak_ptr to shared_prt
        //lock returns a shard_ptr pointing to the object weak_ptr is pointing to
    //if weak_ptr is nullptr, then a shared_ptr with value nullptr is returned.
    cout << *sp1 << endl;
    //cout << *wp1 << endl; Error! weak_ptr does not support * , ->
    cout << *(wp1.lock()) << endl;
    shared_ptr<ThreeD> sp5 = make_shared<ThreeD>(3, 4, 5);
    weak_ptr<ThreeD> wp2 = sp5;
    cout << sp5->ht << endl;
    cout << wp2.lock()->ht << endl;
    sp5.reset(); //the object (3,4,5) will be deleted
    if (wp2.expired()) { cout << "Object has been deleted" << endl; }
    //Reset funcitons are available to all three types of smart pointers
    sp1.reset(new int(40));
    sp1 = make_shared<int>(40);//this is faster than the above
                               //sp1.reset(make_shared<int>(40)); will not compile

                               //weak_ptr<int> wp3 = make_weak<int>(10); make_weak does not exist
    weak_ptr<int> wp3 = make_shared<int>(99);//object 99 is created and immediately deleted because ref count is 0

    cout << wp3.expired() << endl;//1

    linked_list L1 = { 11,2,3,4,5 };
    cout << L1.head->value << endl;

    shared_ptr<linked_list> sp11 = make_shared<linked_list>(initializer_list<int>{ 66, 7, 8, 9, 10 });//notice the
    //new syntax for initializer_list here

    cout << sp11->head->value << endl;
    getchar();
    getchar();
    return 0;
}

2. unique(), swap(), useCount()

//get_unique_swap_UseCount_SmartPointers
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

int main() {
    int * p1 = new int(10);
    shared_ptr<int> sp1(p1);//object 10 has a ref count = 1
    sp1.reset(new int(20));//object 10 will be deleted even though it was
    // pointed by p1; p1 in this case behaves like a weak_ptr
    shared_ptr<int> sp2(new int(100));
    shared_ptr<int> sp3 = make_shared<int>(100);//this is faster than the above
    int * p2 = new int(50);
    shared_ptr<int> sp4(p2);
    if (sp4.get() == p2) cout << "p2 and sp4 point to the same objet" << endl;
    //get() return the raw address
    cout << *p2 << " " << *sp4 << endl;
    shared_ptr<int> sp5 = sp4;
    cout << sp5.use_count() << endl;//use_count() return ref count

    cout << boolalpha << sp5.unique() << endl;
    sp4.reset();
    cout << noboolalpha << sp5.unique() << endl;
    //unique() return true if ref count is 1;otherwise, false
    //boolalpha and noboolalpha are flags which set printing mode
    //for boolean value.
    //boolalpha will have boolean values be printed as true or false
    //noboolalpha will have boolean values be printed as 1 or 0

    shared_ptr<int> sp6 = make_shared<int>(44);
    shared_ptr<int> sp7 = make_shared<int>(55);
    sp7.swap(sp6);
    cout << *sp6 << " " << *sp7 << endl;
        
    int * p10 = new int(25);
    int * p11 = new int(35);
    swap(p10, p11);
    swap(sp6, sp7);//slower than sp6.swap(sp7);

    cout << *p10 << " " << *p11 << " " << *sp6 << " " << *sp7 << endl;
    
    getchar();
    getchar();
    return 0;

}

3. Doubly Linked List using Smart Pointer

#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

template <class T> class ThreeD {
public:
	T ht;
	T wid;
	T dep;
	ThreeD() { ht = wid = dep = 0; }
	ThreeD(T i) { ht = wid = dep = i; }
	ThreeD(T a, T b, T c) { ht = a; wid = b; dep = c; }
	T vol() const { return ht * wid*dep; }
	bool operator==(const ThreeD<T> &t) const { return (vol() == t.vol()); }
	template <class T> friend	ostream & operator<<(ostream &s, const ThreeD<T> &t);
};

template <class T> class node {
public:
	T value;
	//node<T> * next;
	shared_ptr<node<T>> next;
	//node<T> * previous;
	weak_ptr<node<T>> previous;
	//node<T>() { next = nullptr; previous = nullptr; }
	node<T>(){}
	//node<T>(T v) { value = v; next = nullptr; previous = nullptr; }
	node<T>(T v) { value = v; }
	bool operator==(const node<T> &t) const { return value == t.value; }
};
template <class T> class linked_list {
public:
	//node<T> * first;
	//node<T> * last;
	shared_ptr<node<T>> first, last;
	//linked_list() { first = last = nullptr; }
	linked_list() {}
	linked_list(const initializer_list<T> &V);
	void push_front(T t);
	void push_back(T t);
	bool operator==(const linked_list<T> &L) const;
	linked_list(const linked_list<T> &L); //copy constructor
	//~linked_list();//destructor
	void operator=(const linked_list<T> &L);//L-value operator=
	template <class T> friend ostream & operator<<(ostream &s, const linked_list<T> &L);
};
template <class T> linked_list<T>::linked_list(const initializer_list<T> &V) : linked_list() {
	auto it1 = V.begin();
	while (it1 != V.end()) {
		push_back(*it1);
		it1++;
	}
}
/*
template <class T> linked_list<T>::~linked_list() {  //destructor										
	node<T> * p;
	while (first != nullptr) {
		p = first->next;
		delete first;
		first = p;
	}
}
*/
template <class T> linked_list<T>::linked_list(const linked_list<T> &L) : linked_list() { //copy constructor																			  																		  //num_nodes = 0;
	//node<T> * p1 = L.first;
	shared_ptr<node<T>> p1 = L.first;
	//while (p1 != nullptr) {
	while (p1) {
		push_back(p1->value);
		p1 = p1->next;
	}
}

template <class T> void linked_list<T>::operator=(const linked_list<T> &L) { //operator= left ref
	//node<T> * p;
	shared_ptr<node<T>> p;
	first.reset();
	last.reset();
	/*
	while (first != nullptr) {
		p = first->next;
		delete first;
		first = p;
	}
	*/
	p = L.first;
	//while (p != nullptr) {
	while (p) {
		push_back(p->value);
		p = p->next;
	}
}

template <class T> void linked_list<T>::push_front(T t) {
	//node<T> * p = new node<T>(t);
	//shared_ptr<node<T>> p(new node<T>(t)); this is OK
	//shared_ptr<node<T>> p = new node<T>(t); Error!
	shared_ptr<node<T>> p = make_shared<node<T>>(t);
	//if (first == nullptr) { first = last = p; }
	if (!first) { first = last = p; }
	else {
		p->next = first;
		first->previous = p;
		first = p;
	}
}

template <class T> void linked_list<T>::push_back(T t)
{
	//node<T> * p = new node<T>(t);
	shared_ptr<node<T>> p = make_shared<node<T>>(t);
	//if (first == nullptr) { first = last = p; }
	if (!first) { first = last = p; }

	else {
		p->previous = last;
		last->next = p;
		last = p;
	}
}

template <class T> bool linked_list<T>::operator==(const linked_list<T> &L) const {
	int n1 = 0, n2 = 0;
	//node<T> * p;
	shared_ptr<node<T>> p;
	p = first;
	//while (p != nullptr) {
	while (p) {
		p = p->next;
		n1++;
	}
	p = L.first;
	//while (p != nullptr) {
	while (p) {
		p = p->next;
		n2++;
	}
	if (n1 != n2) return false;
	//node<T> * p1 = first, *p2 = L.first;
	shared_ptr<node<T>> p1 = first, p2 = L.first;

	//while (p1 != nullptr) {
	while (p1) {
		if (!(p1->value == p2->value)) { return false; }
		p1 = p1->next;
		p2 = p2->next;
	}
	return true;
}

template <class T> ostream & operator<<(ostream &s, const ThreeD<T> &t) {//ostream & operator<<(ostream &s, ThreeD t) will also work.
	s << "( " << t.ht << ", " << t.wid << ", " << t.dep << " )";
	return s;
}



template <class T> ostream & operator<<(ostream &s, const linked_list<T> &L) {
	//node<T> * p = L.first;
	shared_ptr<node<T>> p = L.first;
	//while (p != nullptr) {
	while (p) {
		s << p->value << " ";
		p = p->next;
	}
	return s;
}

template <class T> ostream & operator<< (ostream &s, const vector<T> & V) {
	s << "[";
	for (size_t i = 0; i < V.size() - 1; i++) {
		s << V[i] << ", ";
	}
	s << V[V.size() - 1] << "]";
	return s;
}


int main() {



	ThreeD<int> td3(3), td4(4), td5(5), td6(6), td7(100, 200, 300);


	linked_list<string>ls_1;
	ls_1.push_front("David");
	ls_1.push_front("John");
	ls_1.push_front("Pat");
	ls_1.push_front("Ben");
	ls_1.push_front("Jeff");
	cout << ls_1 << endl;

	linked_list<string>ls_2;
	ls_2.push_front("Wendy");
	ls_2.push_front("Mary");
	ls_2.push_front("Nancy");
	ls_2.push_front("Jennifer");
	cout << ls_2 << endl;

	ThreeD<double> t10(3.2, 7.4, 8.9), t11(5.6, 7.7, 2.987), t12(4.6, 7.5, 3.1416), t13(55.6, 66.8, 333.45);
	linked_list<ThreeD<double>> LTD1;
	LTD1.push_front(t10);

	LTD1.push_front(t11);
	linked_list<ThreeD<double>> LTD2;
	LTD2.push_front(t13);
	LTD2.push_front(t12);
	LTD2.push_front(t10);
	LTD2.push_front(t11);
	cout << LTD2;

	vector<linked_list<ThreeD<int>>>  V1 = {
		{ { 1,2,3 },{ 4,5,6 } },{ { 11,12,13 },{ 14,15,16 },{ 3,4,5 } },
		{ { 7,8,9 },{ 2,2,3 },{ 4,4,4 } }
	};
	cout << V1 << endl;



	getchar();
	getchar();
	return 0;

}

4. Ring using Smart Pointer

It highlights the advantage of smart pointer when we destruction the object.

#include <iostream>
#include <memory>
using namespace std;
template <typename T> class Node {
public:
	shared_ptr<T> pValue;
	shared_ptr<Node<T>> next;
	Node() {}
	Node(T v) { pValue = make_shared<T>(v); }// pValue.reset(new T(v));  slower
};

template <typename T> class Ring {
public:
	shared_ptr<Node<T>> head;
	//Node<T>* head;
	Ring() {};//Ring() = default;
	//Implement the following functions:
	Ring(const initializer_list<T>& I); //initializer_list
	~Ring();//destructor
	Ring(const Ring<T>& R);//copy constructor
	void operator=(const Ring<T>& R);//Lvalue operator=; copy assignment
	Ring(Ring<T>&& R);//move constructor
	void operator=(Ring<T>&& R);//Rvalue operator=; move assignment
	Ring<T> ThreeTimes();
};

template <class T> Ring<T>::Ring(const initializer_list<T>& I) { //initializer_list
	shared_ptr<Node<T>> p2;
	auto p = I.end() - 1;//const T * p = ...
	while (p != I.begin() - 1) {
		shared_ptr<Node<T>> p1 = make_shared<Node<T>>(*p);
		p1->next = head;
		head = p1;
		if (p == I.end() - 1) p2 = p1;//p2 will point to the last node of Ring
		p--;
	}
	p2->next = head;//form the ring
	cout << "Initializer_list" << endl;
}

template <class T> Ring<T>::~Ring() { //destructor
	if (!head) {
		cout << "Destrutor" << endl;
		return;
	}
	head->next.reset();
	cout << "Destrutor" << endl;
}


template <class T> Ring<T>::Ring(const Ring<T>& R) { //copy constructor
	shared_ptr<Node<T>> p1, p2, p3;
	if (!R.head) return;
	p1 = make_shared<Node<T>>();
	p1->next = head;
	head = p1;
	p3 = p1;
	p2 = R.head->next;
	while (p2 != R.head) {
		p1 = make_shared<Node<T>>();
		p1->next = head;
		head = p1;
		p2 = p2->next;
	}

	p1 = R.head;
	p2 = head;
	while (p2) {
		p2->pValue = make_shared<T>(*p1->pValue);
		p1 = p1->next;
		p2 = p2->next;
	}
	p3->next = head;
	cout << "Copy Constructor" << endl;
}

template <class T> void Ring<T>::operator=(const Ring<T>& R) { //Lvalue assignment, i.e., copy assignment
	if (head) {
		head->next.reset();
		head.reset();//wihtout this, the first node will not be deleted.
	}

	shared_ptr<Node<T>> p1, p2, p3;
	if (!R.head) return;
	p1 = make_shared<Node<T>>();
	p1->next = head;
	head = p1;
	p3 = p1;
	p2 = R.head->next;
	while (p2 != R.head) {
		p1 = make_shared<Node<T>>();
		p1->next = head;
		head = p1;
		p2 = p2->next;
	}
	//p2->next = head;
	p1 = R.head;
	p2 = head;
	while (p2) {
		p2->pValue = make_shared<T>(*p1->pValue);
		p1 = p1->next;
		p2 = p2->next;
	}
	p3->next = head;
	cout << "Copy Assignment" << endl;
}



template<class T> Ring<T>::Ring(Ring<T>&& R) { //Move constructor
	head = R.head;
	R.head.reset();
	cout << "Move Constructor" << endl;
}

template<class T> void Ring<T>::operator=(Ring<T>&& R) {//Rvalue assignment; move assignment
	if (head) {
		head->next.reset();
		head.reset();
	}
	head = R.head;
	R.head.reset();
	cout << "Move Assignment" << endl;
}

template<class T> Ring<T> Ring<T>::ThreeTimes() {
	Ring<T> temp = *this;// copy consturctor
	if (!head) return temp;
	shared_ptr<Node<T>> p1 = temp.head, p2;
	*(p1->pValue) *= 3;
	p2 = p1->next;
	while (p2 != temp.head) {
		*p2->pValue *= 3;
		p2 = p2->next;
	}
	return temp;//return move(temp); return by value;   move constructor
	//a temporary (or hidden) copy is created
	//destructor to remove temp

}


template <class T> ostream& operator<<(ostream& str, const Ring<T>& R) {
	if (!R.head) return str;
	str << *R.head->pValue << " ";
	shared_ptr<Node<T>> p1 = R.head->next;
	while (p1 != R.head) {
		str << *p1->pValue << " ";
		p1 = p1->next;
	}
	return str;
}

int main() {
	Ring<int> R1{ 10,20,30,40,50 };//initializer_list
	//cout << *R1.head->pValue << endl;
	cout << R1 << endl;

	Ring<int> R2{ R1 };//copy constructor
	cout << R2 << endl;

	Ring<int> R3;
	R3 = R1;//copy assignment
	cout << R3 << endl;

	Ring<int> R4;
	R4 = R1.ThreeTimes();//this is pointing to object R1
	//Compiler will convert it to R4.operator=(R1.ThreeTimes());
	//copy constructor -- declare temp and initialize it to R1
	//move constructor -- create hidden (temporary) copy
	//destructor-- delete temp becuase it goes out of scope
	//move assignment -- Assign value from hidden copy to R4
	//destructor-- delete hedden copy because it goes out of scope

	cout << R4 << endl;
	return 0;
}


Extra: const snippet


int i = 10;
//can be re-written as 
//int const * p = &i;
const int * p = &i;
//*p = 100; Error! the object pointed by p is const
//the above can be re-written as int const * p = &i;
i = 250;
//====================================================================================

int * const p = &i;  //pointer p is const, but not the object pointed by p
*p = 1000;
int k = 20;
//p= &k; error.  p is const

const int * const p = &k;  //both pointer and the object pointed by the pointer are const


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值