之前纠结的那个NoName中的指针成员问题,13.5章有集中讨论了
类的指针成员管理:danglingPtr.cpp删除ip后的一些操作和结果: delete ip;std::cout << "After delete: *" << ip << " is " << *ip << std::endl;//证明delete还是有效的,把对象清零了,但是指针指向原位置。ptr.set_ptr_val(220);//虽然强行给原int对象赋值是个灾难std::cout << ptr.get_ptr_val() << std::endl;//但是一个普通的int,disaster显不出来,还能正常用应该用个string之类的13.21题,要写一个析构函数,来删除指针成员。
书中老是说不明白这个问题,前边的NoName应该就是表达错误如果析构中真的有delete *ptr;这种删除对象的析构的话,
int i = 42; HasPtr p1(&i, 42);
直接就内存错误了,至少退出main会析构一次,就错了
关于智能指针--其实就是想办法加一个计数功能,保证所有对象撤销的时候才delete 指针
利用一个“私有类”来完成计数功能
小技巧,U_ptr 的所有成员全是私有,指定友元,使U_ptr完全成为HasPtr的私有类。
未完成--还缺赋值操作符等。。。。
U_Ptr.cpp
//析构函数,删除指针成员,会发生什么? //跟书上学一句话函数体不是好习惯,多了语句一定要转换成正常模式来看 //重点?智能指针只是保障了同一个对象内不能delete两次指针,但是不用copy会出现多个U_Ptr对象,这样每个U_Ptr对象 delete ip;互相都不冲突?为什么? //delete掉的不该是指向的对象么?事实证明也是,析构之后,原p指向的42被清零 #include"head.h" class U_Ptr{ friend class HasPtr; int *ip; size_t use; U_Ptr(int *p): ip(p), use(1) {} ~U_Ptr(){ std::cout << "end:destructor" << std::endl; delete ip;//问题还在这个delete 指针上。 } }; class HasPtr{ public: //copy of the values we're given HasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i){std::cout << "test: *ptr->ip is " << *ptr->ip << std::endl;}//constructor HasPtr(const HasPtr &orig): ptr(orig.ptr), val(orig.val){ ++ptr->use;}//copy members and increment the use count ~HasPtr(){ //为什么程序结束,析构,第一个无hello,第二个有hello,因为if语句导致只有最后一次(由此发现了导致程序错误的if语句失误) if(--ptr->use == 0){ std::cout << "hello" << std::endl;//终于找到毛病了,乱添加测试语句,导致if判断失效,加花括号解决, delete ptr;//错误原因,判断失效,导致删除两次U_Ptr,事实只产生一个U_Ptr,segmental fault } } private: U_Ptr *ptr; int val; }; int main(){ int *p = new int( 42);//问题也不是在这 { HasPtr ptr1(p, 20); std::cout << std::endl; HasPtr ptr2(ptr1); HasPtr ptr3(ptr2); HasPtr ptr4(p, 77);//需要另一个U_Ptr,因此delete ptr两次,两个对象里分别delete一次,但是指向相同却没冲突?想delete多少次都成? std::cout << "---------------" << std::endl; int *p2 = p; int *p3 = p; // delete p2;//同样都是指针,在类外的delete个p2就不行,在类内delete好几次ptr就可以,为什么这么特殊~?????!!! }//delete p的错误是,HasPtr超出scope(main)才析构,所以是先delete p;这样导致类内指针悬垂,避免方法是用个函数块 std::cout << p << " : " << *p << std::endl; //delete p; }
最后是值型类,
class HasPtr{ public: HasPtr(int *p, int i): ptr(new int(*p)), val(i){} HasPtr(const HasPtr &orig): ptr(new int(*orig.ptr)), val(orig.val){} ~HasPtr(){delete ptr;} HasPtr& operator=(const HasPtr &rhs){ *ptr = *rhs.ptr;//不用new个int,只需要把赋值,相当于正常int对象的赋值操作 val = rhs.val; return *this; } //其他操作 int get_ptr_val() const{return *ptr;} int get_int() const{return val;} void set_ptr(int *p){ptr = p;} void set_int(int i){val = i;} //return or change the value pointed to,so ok for const objects int *get_ptr() const{return ptr;} void set_ptr_val(int p) const{*ptr = p;} private: int *ptr; int val; };
Q:每次到底是叫删除对象还是删除指针,如何删除指针?delete是删对象啊,合成析构函数做的:有内置类型的自动撤销,有类类型的析构调用,可是内置类型的指针算什么?书中没说明如果自己写这个函数,要怎么再去调用string和其他类类型的析构函数
A:是删除对象,没错,指针指向的对象。虽说每个类成员是int *ptr;指针而已,看似不储存整型对象,实际容易忘了这局初始化语句,是new一个int对象,让类对象的整型指针指向这个新建的整型对象,析构delete ptr也是删除每个类对象独有的int对象
智能指针的疑惑也在这,因为如果不用复制等手段,不同的HasPtr不一定通过一个U_Ptr对象来指向整型基础对象。所以delete ptr可以指第一个U_Ptr对象也可能是第二个U_Ptr对象。
因为并没有一个机制去检测我新建的HasPtr对象所用的*p是否重复,只能在复制和赋值时检测一下U_Ptr中的计数。
而初始化列表又是U_Ptr(int *p): ip(p), use(1){}这样直接复制的指针,也就是同一个int基础对象原则上讲会被不同的U_Ptr的ip指向,也就是可以被重复~U_Ptr(){delete ip;}啊,怎么解决?
后边13——28要二叉树的相关复制控制函数,先不弄了,要围绕二叉树支持的各种功能来做这个。
最后,小疑问,每次运行,每次初始化这个p,和后续用他来初始化的HasPtr对象的尾地址全是008,018,028,038,此规律何来