C++ 智能指针

2020-01-15 language c/cpp

类似于 C 语言,C++ 中申请的对象也需要手动释放,不像 Java GoLang 这类的语言,有自己的垃圾回收机制 (C++中有相关的实现,但非标准) ,这样 C++ 程序员需要重点关注内存的申请和释放,否则可能会导致内存泄漏。

为了解决 C++ 内存泄漏问题,在 C++11 中引入了智能指针 (Smart Pointer),在一定程度上减轻了对内存的管理。

简介

例如对于如下的函数,如果没有显示的调用 delete(p) 函数,那么就会造成内存泄漏。

#include <unistd.h>
#include <iostream>

class Rectangle {
private:
    double width, height;
};

void foobar(void)
{
    Rectangle *p = new Rectangle();
    //delete(p);
}

int main(void)
{
    while (1) {
        foobar();
        sleep(1);
    }
}

而智能指针实际上就是为了解决上述的问题。

基本使用

一个栈上创建的对象,在退出栈的作用域之后,该对象会自动销毁,而智能指针就是使用的这一原理。创建一个智能指针保存申请好的内存地址,当程序退出作用域的时候,对应的智能指针被自动销毁,同时会释放其指向的内存。

在 C++11 中提供了三种智能指针 std::shared_ptr std::unique_ptr std::weak_ptr ,定义在头文件 <memory> 中,分别用于不同的场景中。

unique_ptr

指向一个唯一的对象,该对象可以 move 但是不能进行赋值。

#include <memory>
#include <iostream>

class Rectangle {
private:
    double width, height;
public:
    Rectangle(double w, double h) {
        width = w;
        height = h;
    }

    double Area(void) {
        return width * height;
    }
};

int main(void)
{
    std::unique_ptr<Rectangle> P1(new Rectangle(10, 5));
    //std::unique_ptr<Rectangle> P2(P1);    // delete copy constructor
    std::cout << P1->Area() << std::endl;   // this'll print 50

    std::unique_ptr<Rectangle> P2;
    P2 = move(P1);
    std::cout << P2->Area() << std::endl;   // this'll print 50
    //std::cout << P1->Area() << std::endl; // cause 'Segmentation fault'

    return 0;
}

shared_ptr

会维护一个引用计数 (可以通过 use_count() 查看),每个 shared_ptr 指向相同的内存,使用引用计数加一,析构时则减一,到 0 时会删除所指向的堆内存。

#include <memory>
#include <iostream>

class Rectangle {
private:
    double width, height;
public:
    Rectangle(double w, double h) {
        width = w;
        height = h;
    }

    double Area(void) {
        return width * height;
    }
};

int main(void)
{
    std::shared_ptr<Rectangle> P1(new Rectangle(10, 5));
    std::cout << P1->Area() << std::endl;   // this'll print 50

    std::shared_ptr<Rectangle> P2;
    P2 = P1;
    std::cout << P2->Area() << std::endl;   // this'll print 50
    std::cout << P1->Area() << std::endl;   // this'll print 50
    std::cout << P1.use_count() << std::endl;

    return 0;
}