C++高頻面試考點 – 智能指針
C++11中引入智能指針的概念,方便堆內存管理。這是因為使用普通指針,容易造成堆內存泄漏,二次釋放,程序發生異常時內存泄漏等問題。
智能指針在C++11版本之后提供,包含在頭文件<memory>
中,shared_ptr
、unique_ptr
、weak_ptr
、auto_ptr
-
shared_ptr
shared_ptr
使用引用計數、每一個shared_ptr
的拷貝都指向相同的內存。每使用它一次,內部的引用計數就加一,每析構一次,內部的引用計數就減一,減為0的時候,自動刪除所指向的堆內存。shared_ptr
內部的引用計數是線程安全的,但是對象的讀取需要加鎖。智能指針是一個模板類,可以指定類型,傳入指針通過構造函數初始化。也可以使用
make_shared
函數初始化。不能將指針直接賦值給一個智能指針,一個是類,一個是指針。例如:
std::shared_ptr <int> p = new int(1);
的寫法是錯誤的。 -
unique_ptr
unique_ptr
“唯一”擁有其所指對象,也就是獨享所有權語義,同一時刻只能有一個unique_ptr
指向給定對象(禁止通過拷貝語義、只有移動語義來實現)。相比于原始指針,unique_ptr
用于其RAII的特性,使得在出現異常的情況下,動態資源能夠得到釋放。unique_ptr
指針本身的生命周期:從unique_ptr
指針創建時開始,直到離開作用域,離開作用域時,若其指向對象,則將其所指對象銷毀。unique_ptr
指針與其所知對象的關系:在智能指針生命周期內,可以改變智能指針所指對象,如創建智能指針時通過構造函數指定、通過reset方法重新指定、通過release方法釋放所有權、通過移動語義轉移所有權。 -
weak_ptr
weak_ptr
是一種不控制對象生命周期的智能指針,它指向一個shared_ptr
管理的對象,進行該對象的內存管理的是哪個強引用的shared_ptr
。weak_ptr
設計的目的是為了配合shared_ptr
而引入的一種智能指針來協助shared_ptr
。這是因為引用計數有一個問題就是互相引用形成環,這樣兩個指針指向的內存都無法釋放。需要weak_ptr
來打破環形引用。如果一塊內存被shared_ptr
和weak_ptr
同時引用,當所有shared_ptr
析構了之后,不管還有沒有weak_ptr
引用該內存,內存也會被釋放。所以weak_ptr
不保證它指向的內存一定時有效的,在使用之前使用函數lock()
檢查weak_ptr
是否為空指針。 -
auto_ptr
auto_ptr
主要是為了解決“有異常拋出時發生內存泄漏”的問題。因為發生異常而無法正常釋放內存。auto_ptr
不支持拷貝和賦值的操作,不能用在STL標準容器中。STL容器中的元素經常要支持拷貝、賦值的操作,在這過程中auto_ptr
會傳遞所有權,所以不能在STL中使用
手撕shared_ptr
#pragma once
namespace my_shared_ptr
{ template <typename T>class shared_ptr{private:/* data */T *m_data;int *m_count; //計數public:shared_ptr() : m_data(nullptr), m_count(nullptr) {}shared_ptr(T *data) : m_data(data) {if(data != nullptr) {m_count = new int(1);}}shared_ptr(const shared_ptr<T> & other) : m_data(other.m_data), m_count(other.m_count){// 拷貝構造函數if(m_data != nullptr){(*m_count) ++;}}shared_ptr(shared_ptr<T> && other) noexcept : m_data(other.m_data), m_count(other.m_count){// 移動構造函數other.m_data = nullptr;other.m_count = nullptr;}~shared_ptr(){if(m_data != nullptr) {(*m_count) --;if(*m_count <= 0){delete m_data;m_data = nullptr;delete m_count;m_count = nullptr;}}}T * get() const{return m_data;}void reset(T *data = nullptr){if(m_data == data){return;}if(m_data == nullptr) {if(data != nullptr){m_data = data;m_count = new int(1);}return;}(*m_count) --;if(*m_count <= 0) {delete m_data;m_data = nullptr;delete m_count;m_count = nullptr;}m_data = data;if(data != nullptr) {m_count = new int(1);}}int use_count() const{if(m_data == nullptr){return 0;}return *m_count;}bool unique() const{// 判斷是否只有一個智能指針指向該對象if(m_data == nullptr){return false;}return *m_count == 1;}void swap(shared_ptr<T> & other){auto data = other.data;auto count = other.m_count;other.m_data = m_data;other.m_count = m_count;m_data = data;m_count = count;}T* operator -> () const{return m_data;}T& operator * () const{return *m_data;}explicit operator bool() const noexcept{return m_data != nullptr;}shared_ptr & operator = (const shared_ptr<T> & other){if(this == &other){return *this;}m_data = other.m_data;m_count = other.m_count;(*m_count)++;return *this;}shared_ptr & operator = (shared_ptr<T> && other) noexcept{if(this == &other) {return *this;}m_data = other.m_data;m_count = other.m_count;other.m_data = nullptr;other.m_count = nullptr;return *this;}};}
測試代碼
#include <string>
#include <iostream>
#include "shared_ptr.h"
using namespace my_shared_ptr;class Test
{
private:std::string m_name;
public:Test(/* args */) = default;void name(const std::string & name);std::string get_name() const;~Test();
};Test::~Test()
{std::cout << "Test is deleted" << std::endl;
}void Test::name(const std::string & name)
{m_name = name;
}std::string Test::get_name() const
{return m_name;
}int main()
{auto p = new Test();shared_ptr <Test> sp(p);sp -> name("jack");std::cout << sp->get_name() << std::endl;std::cout << sp.use_count() << std::endl;shared_ptr <Test> sp2;sp2 = sp;std::cout << sp2.use_count() << std::endl;return 0;
}