【C++】智能指针(一)

这篇文章介绍下C++的智能指针,当然,可能没有你想的那么智能。

为什么需要智能指针1

void remodel(string& str)
{
    string* ps = new string(str);
    str = *ps;
    return;
}

这里不讨论这个函数有没有意义,在这段代码中,很明显,ps没有得到释放。函数的生命周期结束时,临时变量strps都会被销毁,但ps分配的内存还在内存空间里。正确的写法如下:

void remodel(string& str)
{
    string* ps = new string(str);
    str = *ps;
    delete ps;
    return;
}

不需要使用delete[],因为是C++的内置结构。

如果ps有一个析构函数,此函数会在它过期时删除指向的内存那可太好了。但ps只是一个指针,不是对象。而智能指针的思想就是设计一个模板类,代替指针实现上述功能。

auto_ptr、unique_ptr

C++98提出了模板类auto_ptr,但是这个方案在C++11被废弃了,废弃的原因之后我们会讨论,先介绍下这个最早的智能指针。

为了介绍auto_ptr,我们先设计一个类,当其构造函数和析构函数被调用会打印点什么。

class SmartPointer
{
public:
    SmartPointer(string s);
    ~SmartPointer();
};

SmartPointer::SmartPointer(string s)
{
    cout << "SmartPointer Created!" << endl;
}

SmartPointer::~SmartPointer()
{
    cout << "SmartPointer Deleted!" << endl;
}

如下代码会泄露内存,因为SmartPointer的析构函数没有被调用。

int main()
{
    string myStr = "love";
    auto* ps = new SmartPointer(myStr);
}

泄露
使用智能指针,需要包含memory头文件,其格式也和正常的指针不太一样。

auto_ptr<SmartPointer> ps(new SmartPointer("myStr"));

看打印结果,这次析构函数得到了调用。
【C++】智能指针(一)_第1张图片
智能指针的构造函数可以接受常规指针,但是是explicit的,也就意味着无法进行隐式转换。

    auto* ps = new SmartPointer("C++");    
    auto_ptr<SmartPointer> spp = ps; //error
    auto_ptr<SmartPointer> spp2(ps);

接下来讨论一个问题:共享,这是一个智能指针需要处理但是普通指针不需要处理的问题:

    auto_ptr<SmartPointer> sps(new SmartPointer("myStr"));
    auto_ptr<SmartPointer> sps2;
    sps2 = sps;

按照智能指针的设计,两个指针离开生命周期都会释放对应的内存,这就意味着sps2释放的内存是空的。(普通指针不会释放内存所以不会有这个问题,但delete也只能写一个)

有两种办法解决这个问题:

建立所有权概念(ownership)

某些对象只允许一个智能指针拥有它,且如上的赋值操作会转让所有权。这办法应用于auto_ptrunique_ptr,但后者更为严格。

我们为类增加了一个成员变量a,并赋默认值1,然后把上面的代码继续写下去:

    auto_ptr<SmartPointer> sps(new SmartPointer("myStr"));
    auto_ptr<SmartPointer> sps2;
    sps2 = sps;
    cout << sps->a << endl;

代码会引发segment fault,因为sps已经将所有权移交给sps2,现在的sps是空指针。访问空指针的成员变量会出错。不过有一点,如果这里是执行一个成员函数,函数只打印一句话,就没有这个问题,不明白为什么。

unique_ptr

unique_ptr的策略类似于auto_ptr,不过程序会在编译的时候就报错,而不是等到运行的时候再出问题。如果你的IDE足够智能,也会给出提示:
【C++】智能指针(一)_第2张图片

你可能感兴趣的:(C++,工作业务,c++,开发语言)