使用std::auto_ptr的感觉

Sense of using std::auto_ptr

本文关键字:感觉 ptr auto std 使用      更新时间:2023-10-16

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而不是delete[]

唯一的想法是,我不必写delete,但如果指针在我超出范围时会被破坏,那又有什么意义呢。我使用指针来控制变量的生存期。

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

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

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

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

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

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

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

此外。。。

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

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

编译器之所以对传入auto_ptr的函数咆哮,是因为该函数不接受auto_ptr。您需要调用get()来传入指针本身。

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

使用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不得删除,也不得存储

重要信息

请记住,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[]

是的,这是个问题。这也是unique_ptr推出后不再使用auto_ptr的原因之一。它做同样的事情,但使用起来更安全,用途更广泛。

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

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

但请告诉我使用autoptr而不是普通指针有什么意义?

长话短说:许多指针可以指向单个对象存在的各种智能指针都使用类型系统记账,哪个指针拥有对象(=负责释放对象(


旁注

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

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++代码,您还需要编写一个析构函数来在X被销毁时删除ptr

现在好吗?不!因为既然你有一个通用的析构函数,你就需要汇总(或阻止(你自己的复制构造函数和赋值运算符,否则你可能会看到两个X实例指向同一个Type对象,而这两个实例都认为自己拥有这个实例,有时都会试图删除它。Boom,访问冲突。

Unique_ptr不允许隐式复制X对象和对ptr的强引用,因为Unique_ptr是不可复制的(它认为它是该对象的唯一Unique_pr,所以它是唯一负责删除它的智能指针实例——但如果原始的、无所有权的指针指向它,也没关系,只要它们不试图删除自己不拥有的东西!(。

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

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

黄金法则:尽量避免写"delete"(除非你在写自己的容器或智能指针(。:(

如果您可以在堆栈上创建所有对象,则不需要auto_ptr。但想想:

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

转念一想,你可能应该读赫伯·萨特的这篇文章。他知道的比我多

这是正确的方法:

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

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