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;
}
在構造函數中爲指針分配內存,在析構函數中釋放內存,在賦值操作中改變指針所指內容的值而不是指針的值。