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