3

是什么感觉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_ptrfor A[]orchar[]因为 auto_ptr 调用 delete not delete[]

唯一的想法是我不必写delete,但是当我超出范围时它会被破坏,那么指针的意义是什么。我使用指针来控制变量的活动。

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

4

5 回答 5

6

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

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

如果当我超出范围时它会被破坏,那么指针的意义是什么。我使用指针来控制变量的活动。

您从这种心态转变并使用范围来定义变量的生命周期。您只需在适当的范围内声明这些东西,就不必再担心清理工作了。这就是重点。

你的例子是人为的,但你仍然可以看到它为什么有价值。当然,您可能不会忘记调用delete一个什么都不做的两行函数。在实际应用中,事情变得更加复杂。

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

还...

普通变量初始化在栈上,指针在堆上

不完全的。在这种情况下,指针具有自动存储持续时间,但它所指向的却没有。

您的编译器向您传递 anauto_ptr的原因是因为该函数不采用auto_ptr. 您需要调用get()以传入指针本身。

也就是说,你不应该使用auto_ptr任何一个;如果您可以访问 C++ 0x 编译器,请使用unique_ptr ,您应该这样做。

于 2011-10-23T17:58:13.027 回答
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不得删除,也不得存储

于 2011-10-23T17:58:30.713 回答
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推出以来不再使用的原因之一。它做同样的事情,但更安全(=更容易)使用和更通用。

唯一的想法是我不必写delete,但是当我超出范围时它会被破坏,那么指针的意义是什么。

这样您就不会忘记它:-) 或者您可以使用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++ 代码,您还需要编写一个析构函数来ptrX被销毁时删除。

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

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

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

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

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

于 2011-10-23T18:01:45.683 回答
2

如果您有幸在堆栈上创建所有对象,则无需auto_ptr. 但是想想:

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

再三考虑,您可能应该阅读 Herb Sutter 的相关内容。他知道的比我多。;-)

于 2011-10-23T17:57:43.887 回答
1

这是正确的方法:

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

在函数func抛出异常的情况下,auto_ptr当它超出范围时将自动释放内存。

于 2011-10-23T17:58:09.680 回答