C++类的指针成员与其他成员有所不同,指针成员指向一个内存地址,该地址的内存需要我没管理。
我现在分析一下为什么要管理指针成员。
有如下Student类,Student.h如下:
class Student
{
public:
Student(int *books);
virtual ~Student();
int *books;
};
Student.cpp如下:
#include "Student.h"
Student::Student(int *books)
{
this->books=books;
}
Student::~Student()
{
}
在主函数中我如下写:
#include <iostream>
#include "Student.h"
using namespace std;
int main()
{
int *b=new int(34);
Student s(b);
delete b;
cout<<(*(s.books))<<endl;
return 0;
}
当我释放掉了b所指的空间,b的地址就不能用了,但是在s中任然可以访问,所以就出现了不可预知的错误
最后输出的结果如下:
这个指针所指的空间需要我们的Student类来管理,因为我们不知道什么时候该释放掉b的空间,如果手动释放掉b的空间,然后再使用Student对象引用该空间的值,就会出现上面的错误。更复杂的情况是使用一个Student对象初始化另外一个Student对象的时候和赋值的时候。
管理指针成员有两种办法
1.定义智能指针类
2.定义值类型
下面分别介绍
1.智能指针
智能指针:一个行为类似指针但也提供其他功能的类。智能指针的一个通用形式接受指向动态分配对象的指针并负责删除掉该对象,用户分配对象,但由智能指针类删除掉它。智能指针类需要实现复制控制成员来管理指向共享对象的指针。只有在撤销了指向共享对象的最后一个智能指针后,才能删除该共享对象。使用计数是实现智能指针类最常用的方式。
计数类的实现
我先设计一个数据类,目的是察看智能指针的效果。数据类就一个数据,int类型,我主要在析构函数中输出删除了该对象。Flag.h头文件:
class Flag {
public:
Flag(int v);
virtual ~Flag();
int i;
};
Flag.cpp源文件:
#include "Flag.h"
#include <iostream>
Flag::Flag(int v) {
// TODO Auto-generated constructor stub
i=v;
}
Flag::~Flag() {
std::cout<<"delete the value"<<std::endl;
}
下面实现记述类:U_Ptr.h文件:
#include "Flag.h"
class U_Ptr {
public:
friend class Student;
U_Ptr(Flag *p);
virtual ~U_Ptr();
private:
Flag *books;
unsigned int use;
};
U_Ptr.cpp源文件:
#include "UPtr.h"
U_Ptr::U_Ptr(Flag *p) {
this->books=p;
this->use=1;
}
U_Ptr::~U_Ptr() {
delete books;
}
这个类设置Student类为它的友元,以便在Student类中访问该类的私有成员。该类接收一个Flag的指针,该内存在其他地方分配,由U_Ptr类来管理,当该类被释放的时候回收Flag指针所指内存。
下面我完成智能指针类Student,该类决定在什么样的情况下删除Flag指针所分配的内存。
Student.h头文件:
#include "UPtr.h"
#include "Flag.h"
class Student {
public:
Student(Flag *p);
Student(const Student &s);
Student& operator=(const Student &s);
int getValue();
void setValue(int number);
virtual ~Student();
private:
U_Ptr *ptr;
};
该类包含了一个U_Ptr对象的指针,在这个类中,要对复制与赋值进行控制。
Student.cpp源文件:
#include "Student.h"
Student::Student(Flag *p) {
this->ptr=new U_Ptr(p);
}
Student::Student(const Student &s)
{
this->ptr=s.ptr;
++ptr->use;
}
Student& Student::operator=(const Student &s)
{
++s.ptr->use;
if(--ptr->use==0)
{
delete ptr;
}
ptr=s.ptr;
return *this;
}
int Student::getValue()
{
return (ptr->books)->i;
}
void Student::setValue(int number)
{
(ptr->books)->i=number;
}
Student::~Student() {
if(--ptr->use==0)
{
delete ptr;
}
}
在构造函数中,为ptr指针分配内存,但是这个内存在什么时候释放掉要看该ptr的use的计数,当use为0的时候,说明没有其他的Student类共享该ptr所指内存,这时就释放内存。
在赋值的时候,先将右值的计数加一,本对象的计数应该减一,并且判断这时是否应该释放该对象,然后将右值的ptr赋值给本对象。这样如果是相同的对象赋值的话,有安全的保障。如果先减一,刚好计数为1,会释放内存,然后将计数加一。
在主函数中执行下面的代码会得到我希望的结果:
#include <iostream>
using namespace std;
#include "Flag.h"
#include "Student.h"
int main() {
Flag *f=new Flag(43);
Student s1(f);
Student s2(s1);
return 0;
}
打印出 delete the value.
2.定义值类型
这种方法比较简单,看代码了:
Human.h类就是这样的类:
Human.h
class Human {
public:
Human(int value);
Human(const Human &value);
Human& operator=(const Human &value);
virtual ~Human();
private:
int *ip;
};
Human.cpp
#include "Human.h"
Human::Human(int value) {
// TODO Auto-generated constructor stub
ip=new int(value);
}
Human::Human(const Human &value) {
// TODO Auto-generated constructor stub
ip=new int(*(value.ip));
}
Human& Human::operator=(const Human& value)
{
*ip=*(value.ip);
return *this;
}
Human::~Human() {
// TODO Auto-generated destructor stub
delete ip;
}
在构造函数中为指针分配内存,在析构函数中释放内存,在赋值操作中改变指针所指内容的值而不是指针的值。