使用std :: auto_ptr的意义

时间:2011-10-23 17:47:19

标签: c++ pointers smart-pointers auto-ptr

auto_ptr的意义是什么? 看看这段代码:

#include <iostream>
#include <memory>

class A
{
public:
    ~A()
    {
        std::cout << "DEST";
    };
};

void func(A* pa)
{
    std::cout << "A pointer";
}

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    //func(pA);  //compiler error here cannot convert parameter 1 from 'std::auto_ptr<_Ty>' to 'A *' 
    std::cout << "end";
}

int main(int argc, char* argv[])
{
    test();
    return 0;
}

使用此auto_ptr有什么意义?

  • 当超出范围时,它将调用类析构函数作为普通类初始化变量(a)。
  • 我无法将此指针传递给具有类指针(func)
  • 的函数
  • 我无法将指针auto_ptr用于A[]char[],因为auto_ptr调用不会删除delete[]

唯一的想法是我不必编写删除,但是当我超出范围时它是否会被销毁时会产生指针感。我使用指针来控制变量的实时。

普通变量initialize在堆栈上,指针在堆上,但告诉我使用auto_ptr而不是正常指针的意义是什么?

5 个答案:

答案 0 :(得分:5)

  

唯一的想法是我不必写删除

是的,考虑到内存泄漏是一个非常大的好处,这是你将遇到的最常见的错误之一。

  

当我超出范围时,指针的意义是什么?我使用指针来控制变量的实时。

你从这种心态转变并使用范围来定义变量的生命。您只需在适当的范围内声明这些内容,而不必担心清理。这就是重点。

你的例子是人为的,但你仍然可以看出为什么它有价值。当然,您可能不会忘记在两行函数中调用delete,它根本不执行任何操作。在实际应用中,事情变得更加复杂。

当你new一堆对象时,你可以依赖这样一个事实:当函数退出时,对象将被清理掉。课程也是如此;当一个实例超出范围时,你可以依靠它的析构函数来为你解除内存释放。考虑抛出异常时会发生什么。如果没有auto_ptr,你需要确保你处理每一个可能的异常和每个可能的返回路径,以确保你自己清理。

也...

  

正常变量initialize在堆栈上,指针在堆

不完全。在这种情况下,指针具有自动存储持续时间,它指向然而不是。

你的编译器在传递auto_ptr时咆哮的原因是因为该函数没有使用auto_ptr。你需要调用get()传入指针本身。

那就是说,你也不应该使用auto_ptr;如果您有权访问C ++ 0x编译器,请使用unique_ptr

答案 1 :(得分:4)

使用auto_ptr(或其他智能指针)来管理内存有很多原因。我们改变那段代码:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    if(SomeFunc())
      return;
    //func(pA);  //compiler error here cannot convert parameter 1 from 'std::auto_ptr<_Ty>' to 'A *' 
    std::cout << "end";
}

如果pA 不是智能指针,我们现在会发生内存泄漏。但是因为它是,我们知道将正确释放内存。没什么值得担心的。如果SomeFunc抛出异常,它仍然会被释放。

要解决有关传递指针的问题,请执行以下操作:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    func(pA.get());
    std::cout << "end";
}

pA对象仍然拥有内存; func不得删除,且不得存储

答案 2 :(得分:3)

重要

请记住,std::auto_ptr有许多缺点,如果您的编译器提供它,通常应该使用std::unique_ptr。 (如果没有,请更新编译器!:))

现在回到你的问题......


  

是什么意思使用这个auto_ptr?它在超出范围时调用类析构函数作为普通类初始化变量(a)

完全。 auto_ptr的原因是强制执行严格的所有权语义 - 以便在指针本身被销毁时正确销毁对象。

  

我无法将此指针传递给具有类指针(func)

的函数

您可以,您需要使用get()来查找原始指针。将指针传递给函数调用时使用get()意味着该函数不会取得对象的所有权(auto_ptr仍然拥有它并期望它在函数返回后仍然有效)。

或者,您可以在指针上使用release()来获取原始指针,并指示auto_ptr不再负责对象的所有权。

  

我无法将指针auto_ptr用于A []或char [],因为auto_ptr调用delete而不是delete []

是的,这是一个问题。这是自auto_ptr引入以来不再使用unique_ptr的原因之一。它做同样的事情,但更安全(=更容易)使用和更多功能。

  

唯一的想法是我不必编写删除,但是当我超出范围时它是否会被驱逐,那么指针的感觉是什么。

这样你就不会忘记它:-)或者你可以使用auto_ptr(或更好的unique_ptr作为类对象中的成员。)

  

但请告诉我使用auto_ptr而不是正常指针的意义是什么?

长话短说:许多指针可以指向单个对象。 存在各种智能指针,使用类型系统进行簿记,指针拥有对象(=负责释放它)。


附注

如果你有一个(可能)拥有另一个对象实例的类,你只需写:

class X {
    // ...
    X() : ptr(new Type()) {}
    X(Type ptr) : ptr(ptr) {}
    // ...
    void setPtr(Type ptr2) { ptr.reset(ptr); }
    // ...
    std::unique_ptr<Type> ptr;
};

如果设置了ptr,那么例如unique_ptr的析构函数将负责删除对象(如果有的话)。在setPtr方法中,reset()函数将删除旧实例(如果有)并将成员设置为提供的新实例(或者为null - 这没关系。)

现在比较另一种情况:

class X {
   // ...
   X() : ptr(new Type()) {}
   X(Type ptr) : ptr(ptr) {}
   // ...
   void setPtr(Type ptr2) {delete ptr; ptr = ptr2;}
   // ...
   Type* ptr;
};

同样的行为?没有!因为现在,为了拥有安全的C ++代码,您还需要编写一个析构函数,以便在ptr被销毁时删除X

好的,现在好吗?没有!因为你有一个共同的析构函数,你需要汇总(或阻塞)你自己的复制构造函数和赋值运算符,因为否则你最终会得到两个指向同一个Type对象的X实例 - 这两个实例都认为他们拥有这个实例,并且有时会尝试删除它。繁荣,访问违规。

Unique_ptr不允许您隐式复制X对象以及对ptr的强引用,因为unique_ptr是不可复制的(它认为它是对象的唯一unique_ptr,所以它是只有智能指针实例负责删除它 - 但是如果原始的,非拥有的指针指向它,只要它们不尝试删除它们不拥有的东西就可以了!)。

这不是全部 - unique_ptr无法复制,但它有一个移动构造函数和移动赋值运算符为您准备好了!因此,您可以安全地从函数等返回它。

这就是智能指针的类型安全性如何转化为编写更安全的代码。

黄金法则:尽量避免编写“删除”(除非您正在编写自己的容器或智能指针)。 :)

答案 3 :(得分:2)

如果您能够在堆栈中创建所有对象,那么就不需要auto_ptr。但想想:

  • 从函数返回一个对象(你不能返回一个本地)
  • 确保从函数返回的对象将被调用者接收或在出现异常时被销毁
  • 聚合指向类内对象的指针(您需要确保析构函数删除它,或者您只需将其设为auto_ptr以防止内存泄漏,期间)
  • ......还有更多

第二个想法,你应该read Herb Sutter on this。他比我更了解。 ; - )

答案 4 :(得分:1)

这是正确的方法:

void test()
{
    A a;
    std::auto_ptr<A> pA(new A);
    func(pA.get());
    std::cout << "end";
}

如果函数func抛出异常,auto_ptr会在超出范围时自动释放内存。