为什么要使用智能指针?

时间:2013-06-12 09:50:47

标签: c++ smart-pointers

特别是智能指针好用,如果不鼓励使用C ++中的指针,为什么还要使用智能指针呢?举个例子:

class Object {  }

smart_pointer < Object > pInstance;
//wherein I can use the very simple way
Object instance;

2 个答案:

答案 0 :(得分:5)

当您需要维护对象的所有权时,智能指针很好。使用它们将确保适当的破坏。当指针被视为参考时,使用智能指针有时会更糟(例如,在性能方面)。

指针是C ++的重要组成部分,但使用它们变得更加容易,因为C ++ 11中的智能引入了移动语义(基本上使unique_ptr成为可能)。这就是为什么在现代代码中,如果可用,您应该始终使用std::unique_ptrstd::shared_ptr


编辑:你问了一个例子,指针有利于使用。我能想到的最常见的问题是某个系统的可选组件。该组件将使用大量内存,因此您不希望始终分配它,也不要控制其分配(因此它本身不能处于“空”状态,即不可为空)。 Boost.Optional和C ++ 14-ish std::optional分配TOD的POD-ish大小的内存,所以他们不会这样做。使用指针,您可以选择分配该内存:

class Outer { 
    std::unique_ptr<BigInner> optionalComponent;
public:
    void initializeOptionalComponent() { 
        optionalComponent = new BigInner();
    }
    void useOptionalComponent() {
        if (!optionalComponent)
            // handle the error
        // operate
    } 
};

这将解决问题,但会引入明显的另一个问题:optionalComponent可以为null,这要求所有使用它的函数始终检查有效状态。如果它是一个简单的按价值成员,它将(或至少应该)始终处于有效状态。因此,如果您不需要指针,根本不使用它,请使用vector<MyClass>和普通成员。

无论如何,在这种情况下使用智能指针可以让你保持零规则;您不必编写析构函数,复制构造函数或赋值运算符,该类将安全地运行。

答案 1 :(得分:1)

快速回答:智能指针对于(特别是)

非常有用
  • 执行RAII
  • 管理指针所有权

执行RAII

程序中的指针和原因(在很多方面,一些明显的,一些扭曲的)崩溃的一个问题是你负责它们下面的内存。这意味着当您动态分配内存(通过new)时,您负责此内存,并且不要忘记调用delete。这意味着它会发生,更糟糕的是,即使您没有忘记,也会永远无法达到删除语句。 请考虑以下代码:

void function(){
    MyClass* var = new MyClass;

    //Do something

    delete var;
}

现在,如果此函数在达到delete语句之前抛出异常,则不会删除指针......内存泄漏!

RAII是一种避免这种情况的方法:

void function(){
    std::shared_ptr<MyClass> var(new MyClass);

    //Do something

    //No need to delete anything
}

指针由对象保存,并在其析构函数中删除。与前面代码的区别在于,如果函数抛出异常,将调用共享指针的析构函数,因此将删除指针,避免内存泄漏。

RAII利用了以下事实:当局部变量超出范围时,会调用其dtor。

管理指针所有权

注意我在前面的例子中使用了哪个智能指针。 std::shared_ptr是一个智能指针,在传递指针时非常有用。如果您的代码的许多部分需要指向同一对象的指针,那么确定应该删除它的位置可能会很棘手。您可能想要在某处删除指针,但如果您的代码的另一部分正在使用它,该怎么办?它导致访问已删除的指针,这根本不可取! std::shared_ptr有助于防止这种情况发生。当你传递一个shared_ptr时,它会跟踪代码中有多少部分引用它。当没有对指针的任何引用时,该指针删除内存。换句话说,当没有人再使用指针时,它会被安全删除

还有其他类型的智能指针可以解决其他问题,例如std::unique_ptr提供指针,指针是其下方指针的唯一所有者。

注 - 关于多态的小解释

需要指针来使用多态。如果你有一个抽象类MyAbstract(也就是说,它至少有一个虚拟的,比如说doVirtual()),它就无法实例化。以下代码:

MyAbstract var;

将无法编译,您将从编译器获得Can't instantiate abstract class行的内容。

但这是合法的(ImplAImplB同时从MyAbstract公开继承:

MyAbstract* varA = new ImplA;
MyAbstract* varB = new ImplB;

varA->doVirtual(); //Will call ImplA implementation
varB->doVirtual(); //Will call ImplB implementation

delete varA;
delete varB;