Smart pointer in C++
什么是smart pointer
一般在C/C++中,如果我们使用了pointer来指向某块heap区域,当不再需要这块区域的时候,我们需要手动删除它。如果忘了的话,就会产生memory leak。比如:
int* a = new int[5];
// no memory leak
delete[] a;
int* b = new int[5];
// memory leak
手动删除太麻烦,又容易出错,那么能不能有一种pointer,当我们不需要它之后,它能帮我们自动删除指向的内存区域呢?先不提C++所提供的smart pointer,假设我们自己想设计这样一个pointer,可以这样做:
class smartPtr{
public:
obj* ptr; // ptr是指向任何类型的obj的指针
smartPtr(obj* p): ptr(p){
}
~smartPtr(){
delete ptr;
}
};
有了这样smartPtr,我们可以这样使用它:
int main(){
{
// constructor smartPtr()被调用
// p被初始化为指向obj的一个pointer
smartPtr p = new obj();
}
//当p退出{}这个scope, p的destructor ~smartPtr()会被调用
//而在~smartPtr()中,p指向的obj被自动delete了
}
可以看见,我们不需要手动调用delete来进行删除了,因为smartPtr替我们做好了这一切。而这就是C++ smart pointer的基本思想(当然,C++提供的smart pointer要fancy得多)。
Smart pointer像是一个owner。当它退出当前scope之后,它的destructor会被调用,而它的destructor会自动删除它所指向的Object。
为什么要使用smart pointer
其实在介绍什么是smart pointer的时候,就已经说明为什么要使用smart pointer了。不过这个部分我们再进行进一步地阐述。
假设我们有一个base class GeoObj,以及两个继承了GeoObj的subclasses Circle和Line:
class Coord{
private:
int x, y;
public:
Coord(int _x, int _y):x(_x), y(_y){
}
};
class GeoObj{
public:
virtual void move(Coord) = 0;
virtual void draw() const = 0;
virtual ~GeoObj() = default;
};
class Line : public GeoObj{
private:
Coord from;
Coord to;
public:
Line(Coord f, Coord t): from(f), to(t){
std::cout << "Line object is created!" << std::endl;
}
void move(Coord) override {
}
void draw() const override {
}
~Line() override{
std::cout << "Line object is deleted!" << std::endl;
}
};
class Circle: public GeoObj{
private:
Coord center;
int rad;
public:
Circle(Coord c, int r): center(c), rad(r){
std::cout << "Circle object is created!" << std::endl;
}
void move(Coord) override {
}
void draw() const override {
}
~Circle() override {
std::cout << "Circle object is deleted!" << std::endl;
}
};
然后我们创建一个存储GeoObj指针的数组 fig,并且向里面加入一个Line object和一个Circle Object。
std::vector<GeoObj*> fig;
Line* l = new Line(Coord(1,2), Coord(3,4));
Circle* c = new Circle(Coord(5,6), 7);
fig.push_back(l);
fig.push_back(c);
接下来,如果我们想要删除fig中的元素,有的人可能会想到直接使用fig.clear()。但是fig.clear()只会删除lp, cp这两个指针,却不会删除这两个指针指向的元素Line和Circle,所以如果大家试一下的话,可以在terminal看到Line和Circle的constructor被调用,destructor却没有没调用。也就是说,我们删除了指向这两个objects的指针,却没有回收这两个指针指向的内存区域。这样导致的问题就是在Garbage Collector眼中,这两块内存区域已经被某个程序使用了,但其实