C++11--智能指針詳解及實現

本文主要參考:

1)《Effective Mordern C++》

2)《C++ Primer》

3) https://en.cppreference.com/w/cpp/memory/shared_ptr  --可在線做實驗測試代碼

1、shared_ptr的實現原理 

智能指針的一種通用實現技術是使用引用計數。智能指針類將一個計數器與智能指針指向的對象相關聯,用來記錄有多少個智能指針指向相同的對象,並在恰當的時候釋放對象

每次創建類的新對象時,初始化指針並將引用計數置爲1;當對象作爲另一對象的副本而創建時,引用計數加1;對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數(如果引用計數爲減至0,則刪除對象),並增加右操作數所指對象的引用計數;調用析構函數時,構造函數減少引用計數(如果引用計數減至0,則刪除基礎對象)。

下圖是shared_ptr的內存模型:

由圖中可以看到,實際上引用計數、自定義銷燬等都不是直接存儲在shared_ptr中,而是通過一個指針指向的一個控制塊存儲的,控制塊是動態分配的內存 。

下面是用類模板簡單實現shared_ptr.

#include <iostream>
using namespace std;

template<typename T>
class SmartPointer {
private:
	T* p_;
	size_t* count_;
public:
	SmartPointer(T* ptr = nullptr) : p_(ptr) {
		if (p_) {
			count_ = new size_t(1);
		}
		else {
			count_ = new size_t(0);
		}
	}

	SmartPointer(const SmartPointer& ptr) {
		p_ = ptr.p_;
		count_ = ptr.count_;
		(*count_)++;
	}

	SmartPointer& operator=(const SmartPointer& ptr) {
		if (p_ == ptr.p_) {
			return *this;
		}

		if (p_) {
			if (--(*count_) == 0) {
				delete p_;
				delete count_;
			}
		}

		p_ = ptr.p_;
		count_ = ptr.count_;
		(*count_)++;
		return *this;
	}

	~SmartPointer() {
		if (--(*count_) == 0) {
			delete p_;
			delete count_;
		}
	}

	size_t use_count() {
		return *count_;
	}
};

int main() {
	SmartPointer<int> sp1(new int(10));
	SmartPointer<int> sp2(sp1);
	SmartPointer<int> sp3(new int(20));
	sp2 = sp3;
	std::cout << sp1.use_count() << std::endl;
	std::cout << sp3.use_count() << std::endl;
}

2、智能指針帶來的性能影響

1)shared_ptr的尺寸是裸指針的兩倍。 

2)會帶來控制塊的開銷。

3)引用計數的遞增和遞減是原子操作,原子操作一般都比非原子操作慢。

3、使用weak_ptr解決shared_ptr的循環引用

 解決方案:將類A,B中的shared_prt改爲weak_ptr即可,weak_ptr不會增加shared_ptr的引用計數

class B;
class A {
public:
   weak_ptr<B> p;
   //shared_ptr<B> p;
};
class B {
public:
   weak_ptr<A> p;
   //shared_ptr<A> p;
};
int main() {
  while (true) {
     shared_ptr<A> pa(new A());
     shared_ptr<B> pb(new B());
      pa->p = pb;
      pb->p = pa;
   }
  return 0;
}

4 與shared_ptr關係緊密的幾個函數

1)make_shared函數

作用:此函數在動態內存中分配一個對象並初始化它,返回指向此對象的shared_ptr。

用法:make_shared用其參數來構造給定類型的對象,如果不傳遞任何參數,對象就會進行值初始化。

// 指向一個值爲42的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);
// p4指向一個值爲"9999999999"的string
shared_ptr<string> p4 = make_shared<string>(10,'9');
// p5指向一個值初始化的int,即,值爲0
shared_ptr<int> p5 = make_shared<int>();
// p6指向一個動態分配的空vector<string>
auto p6 = make_shared<vector<string>>();

2)get函數

作用:向不能使用智能指針的代碼傳遞一個內置指針。

注意:get用來將指針的訪問權限傳遞給代碼,你只有在確定代碼不會delete指針的情況下,才能使用get。特別是,永遠不要用get初始化另一個智能指針或者另一個智能指針賦值。

// shared_ptr::get example
#include <iostream>
#include <memory>

int main () {
  int* p = new int (10);
  std::shared_ptr<int> a (p);

  if (a.get()==p)
    std::cout << "a and p point to the same location\n";

  // three ways of accessing the same address:
  std::cout << *a.get() << "\n";
  std::cout << *a << "\n";
  std::cout << *p << "\n";

  return 0;
}

 3)reset函數

作用:重置指針,將一個新的指針賦予一個shared_ptr.

// shared_ptr::reset example
#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp;  // empty

  sp.reset (new int);       // takes ownership of pointer
  *sp=10;
  std::cout << *sp << '\n';

  sp.reset (new int);       // deletes managed object, acquires new pointer
  *sp=20;
  std::cout << *sp << '\n';

  sp.reset();               // deletes managed object

  return 0;
}

參考博客:

1. https://www.cnblogs.com/wxquare/p/4759020.html

2. https://segmentfault.com/a/1190000006860811

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章