C++學習中遇到的問題,記錄之。使用的教材: 《C++ primer》。
智能指標。what。 how。
What ‘s the smartpointer and how to impletment it?
什麼是智能指標。
通過引用計數,自動管理動態分配的記憶體的生存期,避免記憶體流失或懸垂指標的方法。
應用情境:
如果類的一個成員是指向動態分配的記憶體塊的指標,在做複製控制的時候,我們有兩種方法:
1、複製指標指向的記憶體塊,為了避免記憶體流失,會在類的解構函式裡釋放記憶體。這樣為造成記憶體空間的浪費,copy操作也會帶來不必要的開銷。
2、只複製指標。但引入了新的問題:當通過一個副本對象的指標成員釋放了這個記憶體塊之後,其它指標就成了所謂的懸垂指標。
這裡引入智能指標,來解決2 中帶來的問題。
如何?智能指標。
為瞭解決這個問題,我們在方法2裡,加入引用計數的方法,來管理這個對象的記憶體釋放。
假設我們這裡需要管理的是一個int型指標ip指向的記憶體。如下圖:
通過U_ptr來將指標和一個引用計數綁定在一起,然後類HasPtr通過一個指向U_ptr的指標成員來間接訪問 ip 指向的記憶體。
(這裡應該限制,一個記憶體塊只對應一個U_ptr的對象)
U_ptr的引用計數 use 初始化為1.
所有對類HasPtr的對象的複製控制操作,都會修改U_ptr的引用計數。
每次對這個HasPtr對象的copy,都不copy 指標 ip 指向的記憶體塊,但對U_ptr的use加一。
每一個對象的解構函式中,都判斷use減一後是否為0,如果是,則銷毀U_ptr對象綁定的那個記憶體塊。
//C++ primer plus ...... chapter 8 page253#include <iostream>#include <string>#include <new>using namespace std;class U_Ptr{private:friend class HasPtr;int *ip;size_t use;U_Ptr(int *p):ip(p),use(1){ }
~U_Ptr() {cout << "U_Ptr destructor...free mem ip = " << ip << endl;delete ip; }};class HasPtr{public:HasPtr(const string &str, int *p):name(str),ptr(new U_Ptr(p)) {cout << name << "\tHasPtr do normal construct" << endl;}HasPtr(const string &str, const HasPtr &orig):name(str),ptr(orig.ptr) {cout << name << "\tHasPtr do copy construct" << endl;++ptr->use; }//some friend operatorsfriend std::ostream& operator<<(std::ostream& , const HasPtr &);HasPtr& operator=(const HasPtr &orig){cout << name << "\tHasPtr do assignment from "<< orig.name << endl;if(orig.ptr == this->ptr){cout << "They are the same one, do nothing" << endl;return *this;}++orig.ptr->use; if(--ptr->use == 0){cout << name << "\tdelete ptr "<< endl;delete ptr;}else {cout << name << "\tptr usecnt = " << ptr->use << endl;}ptr = orig.ptr;return *this;}~HasPtr(){--ptr->use;cout << name << "\tHasPtr destructor ... ptr usecnt = " << ptr->use << endl;if( ptr->use == 0 ){delete ptr; //here call U_Ptr's destructor}}//some apiint * get_ptr() const {return ptr->ip ;}int get_ptr_val() const { return *(ptr->ip) ;}void set_ptr(int *p) { ptr->ip = p ;}void set_ptr_val(int i) { *ptr->ip = i ;}private:string name;U_Ptr *ptr;};std::ostream& operator<<(std::ostream& os , const HasPtr &hp){os << hp.name <<"\tval = "<< hp.get_ptr_val() << "\taddr = "<< hp.get_ptr();return os;}//overloaded the operator "<<".int main(int argc, char const *argv[]){int *a = new int(123);cout << endl << "normal constructor" << endl;HasPtr cp1("cp1", a);cout << cp1 << endl;cout << endl << "copy constructor" << endl;HasPtr cp2("cp2",cp1);cp2.set_ptr_val(199);cout << cp2 << endl;cout << endl << "assignment operator " << endl;cp2 = cp1 ;cout << cp2 << endl;cout << endl << "more HasPtr to a" << endl;HasPtr cp3("cp3" , a);HasPtr cp4("cp4" , a);cout << cp3 << endl;cout << cp4 << endl;if(a!=NULL)cout << "a is still keep the mem, addr = " << *a << endl;elsecout << "a has been free mem" << endl;cout << endl << ".................exit from main" << endl;//delete(a); //do not need free mem herereturn 0;}
在linux下,使用g++編譯,運行結果:
normal constructorcp1HasPtr do normal constructcp1val = 123addr = 0x9f0d008copy constructorcp2HasPtr do copy constructcp2val = 199addr = 0x9f0d008assignment operator cp2HasPtr do assignment from cp1They are the same, do nothingcp2val = 199addr = 0x9f0d008more HasPtr to acp3HasPtr do normal constructcp4HasPtr do normal constructcp3val = 199addr = 0x9f0d008cp4val = 199addr = 0x9f0d008a is still keep the mem, addr = 199.................exit from maincp4HasPtr destructor ... ptr usecnt = 0U_Ptr destructor...free mem ip = 0x9f0d008cp3HasPtr destructor ... ptr usecnt = 0U_Ptr destructor...free mem ip = 0x9f0d008cp2HasPtr destructor ... ptr usecnt = 1cp1HasPtr destructor ... ptr usecnt = 0U_Ptr destructor...free mem ip = 0x9f0d008