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 operators friend 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 api int * 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; else cout << "a has been free mem" << endl; cout << endl << ".................exit from main" << endl; //delete(a); //do not need free mem here return 0; }
在linux下,使用g++编译,运行结果:
normal constructor cp1 HasPtr do normal construct cp1 val = 123 addr = 0x9f0d008 copy constructor cp2 HasPtr do copy construct cp2 val = 199 addr = 0x9f0d008 assignment operator cp2 HasPtr do assignment from cp1 They are the same, do nothing cp2 val = 199 addr = 0x9f0d008 more HasPtr to a cp3 HasPtr do normal construct cp4 HasPtr do normal construct cp3 val = 199 addr = 0x9f0d008 cp4 val = 199 addr = 0x9f0d008 a is still keep the mem, addr = 199 .................exit from main cp4 HasPtr destructor ... ptr usecnt = 0 U_Ptr destructor...free mem ip = 0x9f0d008 cp3 HasPtr destructor ... ptr usecnt = 0 U_Ptr destructor...free mem ip = 0x9f0d008 cp2 HasPtr destructor ... ptr usecnt = 1 cp1 HasPtr destructor ... ptr usecnt = 0 U_Ptr destructor...free mem ip = 0x9f0d008