普通指針:指向內存區域的地址變量。使用普通指針容易出現一些程序錯誤。
如果一個指針所指向的內存區域是動態分配的,那么這個指針變量離開了所在的作用域,這塊內存也不會自動銷毀。動態內存不進行釋放就會導致內存泄露。如果一個指針指向已經釋放的區域,那么這個指針就是一個懸空指針,使用懸空指針會 造成不可預料的結果。定義了一個指針,卻未初始化實際指向有效的內存區域,這個指針就成了野指針。使用野指針訪問內存一般會造成段錯誤。
使用智能指針可以有效避免上述錯誤的發生。
智能指針:封裝了動態對象指針的類對象。
?由于智能指針是一個對象,它封裝了一個指向另一個對象的指針。當智能指針離開作用域后,會被自動銷毀。銷毀過程中會調用析構函數,來刪除封裝的對象。
標準的模板庫中提供了以下幾種智能指針。
unique_ptr
template<
class T,
class Deletr=std::std::default_delete<T>
>class unique_ptr
T是所封裝的動態對象分配類型
Deleter是unique_ptr在釋放它所管理的對象時,所使用的方法。一般我們使用默認值。template<
class T,
class Deleter
>class unique_prt<T[],Deleter>
針對動態數組的特化版本。
以下是unique_ptr的常用函數。T* get(); //獲得所管理對象指針
T* operator->();//重載的間接運算符調用了get函數,也返回了所管理對象的指針,這樣可以使用間接成員運算符來訪問所管理的對象成員了
T& operator*();//重載的解引用運算符返回所管理的對象的引用,相當于*get()函數
T* release();//接觸對封裝對象的管理,返回對象的指針,這個對象指針脫離unique_ptr,c成為一個普通指針,用完這個指針需要手動釋放。
void reset(T* newObject);//刪除原有的對象,接管新的對象
void swap(unique_ptr<T>&other);與其他的unique_Ptr對象互換。
unique_ptr與它所管理的對象是動態一對一的關系,不能有兩個unique_ptr對象指向同一個地址。
創建一個unique對象的方法是
unique_ptr<A>ptr1(new A(參數))
unique_ptr<A>ptr=make_unique<A>(參數)
?
#include<memory>
#include<iostream>using namespace std;
class Rectangle
{
public:Rectangle(double w,double h):width(w),height(h) {}~Rectangle() { cout << "對象封裝被釋放" << endl; }double area(){return width * height;}
private:double width;double height;
};
int main()
{using std::unique_ptr;{unique_ptr<Rectangle>pDemo(new Rectangle(3,4));cout << pDemo->area() << endl;}
}
由于智能指針重載了間接成員運算符和解引用運算符,它們會返回智能指針所包含對象的指針或者引用,可以像使用普通指針一樣使用智能指針。除了在離開當前作用域時會刪除指針指向的對象,下面幾種方法也會刪除
unique_ptr<Rectangle>p1(new Rectangle(1, 1));
p1 = nullptr;unique_ptr<Rectangle>p1(new Rectangle(1, 1));
unique_ptr<Rectangle>p2(new Rectangle(1, 1));
p1 = move(p2);unique_ptr<Rectangle>p1(new Rectangle(1, 1));
p1.reset(new Rectangle(3, 7));
由于unique_ptrd對管理的資源具有獨占性,所以unique_ptr不能被拷貝,也不能被賦值。不過可以對unique_ptr對象所管理對象所有權進行轉移。
#include<memory>
#include<iostream>using namespace std;
class Rectangle
{
public:Rectangle(double w,double h):width(w),height(h) {}~Rectangle() { cout << "對象封裝被釋放" << endl; }double area(){return width * height;}
private:double width;double height;
};
int main()
{unique_ptr<Rectangle>p1(new Rectangle(1,3));unique_ptr<Rectangle>p2 = move(p1);cout << p2->area() << endl;}
?上述代碼中通過move函數p2擁有了p1對象管理權。p1包含了一個空指針。這時可以通過p2來訪問它所封裝的對象成員了。
unique_ptr主要適合在使用在普通指針的地方,例如使用在容器上,
struct Packet
{Packet(long id) :m_id(id) {}long m_id;char Data[1000];
};
struct Compare {bool operator()(const Packet& a, const Packet& b){return a.m_id < b.m_id;}
};
void sortValueVector(int n)
{vector<Packet>vecPacket;for (int i = 0; i < n; i++){vecPacket.push_back(Packet(rand() % n));}sort(vecPacket.begin(), vecPacket.end(), Compare());
}
用vector裝入n個Packet對象,然后對他進行排序。由于容器中裝入的是對象,對于這種較大的對象,排序意味著大量數據進行移動復制,這樣開銷很大。如果將容器中的對象改為指針?,排序時僅涉及到指針值的復制。那么效率會高很多。如下代碼
struct Packet
{Packet(long id) :m_id(id) {}long m_id;char Data[1000];
};
struct Compare {bool operator()(const Packet* pA, const Packet* pB){return pA->m_id < pB->m_id;}
};
void sortValueVector(int n)
{vector<Packet*>vecPacket;for (int i = 0; i < n; i++){vecPacket.push_back(new Packet(rand() % n));}sort(vecPacket.begin(), vecPacket.end(), Compare());
}
使用指針的缺點是需要使用專門的代碼對指針維護,當刪除,替換時,需要釋放不再使用的指針對象, 如果出現異常,提前返回等情況,容易造成內存泄露。如果將容器中的指針替換成unique_ptr,不僅獲得接近普通性能的智能指針,還實現了內存資源的自動釋放,不會出現意外的內存泄露情況。
下面這段代碼中針對compare 類針對對象,指針,智能指針,三種情況下,排序所用時間
#include<memory>
#include<iostream>
#include<vector>
#include<algorithm>
#include<chrono>
using namespace std;
using namespace std::chrono;
struct Packet
{Packet(long id) :m_id(id) {}long m_id;char Data[1000];
};
struct Compare {bool operator()(const Packet& a, const Packet& b){return a.m_id < b.m_id;}bool operator()(const Packet* pA, const Packet* pB){return pA->m_id < pB->m_id;}//template<template<typename>typename SmartPtr>bool operator()(const unique_ptr<Packet>& pA, const unique_ptr<Packet>& pB){return pA->m_id < pB->m_id;}
};
class AutoToTimer {
private:high_resolution_clock::time_point startTime;string description;
public:AutoToTimer(const char* desc) :description(desc) {startTime = high_resolution_clock::now();}~AutoToTimer(){high_resolution_clock::time_point endTime = high_resolution_clock::now();auto duration = duration_cast<chrono::microseconds>(endTime - startTime).count();cout << description << ":" << duration << "ms" << endl;}
};
void sortValueVector(vector<int>ids)
{vector<Packet>vecPacket;for (auto id : ids){vecPacket.push_back(Packet(id));}{ AutoToTimer autoTimer("sortValueVector");sort(vecPacket.begin(),vecPacket.end(),Compare());}
}void sortPointVector(vector<int>ids)
{vector<Packet*>vecPacket;for (auto id : ids){vecPacket.push_back(new Packet(id));}{AutoToTimer autoTimer("sortPointPtr");sort(vecPacket.begin(), vecPacket.end(), Compare());}
}
template<typename SmartPtr>
void sortSmartPtrVector(vector<int>ids)
{vector<SmartPtr>vecPacket;for (auto id : ids){vecPacket.push_back(SmartPtr(new Packet(id)));}{AutoToTimer autoTime("sortUniquePtrVector");sort(vecPacket.begin(),vecPacket.end(),Compare());}
}
int main()
{int n = 100000;vector<int>randomId{ n,0 };for (int i = 0; i < n; i++){randomId.push_back(rand() % 100000);}sortValueVector(randomId);sortPointVector(randomId);sortSmartPtrVector<unique_ptr<Packet>>(randomId);
}
打印結果
?
?
?