C++智能指针--auto_ptr指针

auto_ptr是C++标准库提供的类模板,头文件<memory>,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。auto_ptr不支持new数组。

auto_ptr的出现,主要是为了解决“有异常抛出时发生内存泄漏”的问题。如下的简单代码是这类问题的一个简单示例。

int* p = new int(100);
try
{
    doSomething();

    cout << *p << endl;

    delete p;
}
catch(exception& e)
{

}

当doSomething();部分抛出异常,将导致指针p所指向的空间得不到释放而导致内存泄露。auto_ptr的引入解决了这类问题。

初始化auto_ptr对象的方法?

auto_ptr构造时取得某个对象的所有权,在析构时释放该对象。我们实际上是创建一个auto_ptr<Type>类型的局部对象,该局部对象析构时,会将自身所拥有的指针空间释放,所以不会有内存泄露。

auto_ptr<int> p(new int(1));//推荐

//或
int* np = new int(1);
auto_ptr<int> p(np);

1) 构造函数

1] 将已存在的指向动态内存的普通指针作为参数来构造

int* p = new int(33);

auto_ptr<int> api(p);

2] 直接构造智能指针

auto_ptr< int > api( new int( 33 ));

2) 拷贝构造

利用已经存在的智能指针来构造新的智能指针

auto_ptr< string > pstr_auto( newstring( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2(pstr_auto );  //利用pstr_auto来构造pstr_auto2

因为一块动态内存智能由一个智能指针独享,所以在拷贝构造或赋值时都会发生拥有权转移的过程。在此拷贝构造过程中,pstr_auto将失去对字符串内存的所有权,而pstr_auto2将其获得。对象销毁时,pstr_auto2负责内存的自动销毁。

3) 赋值

利用已经存在的智能指针来构造新的智能指针

auto_ptr< int > p1( new int( 1024) );

auto_ptr< int > p2( new int( 2048) );

p1 = p2;

在赋值之前,由p1 指向的对象被删除。赋值之后,p1 拥有int 型对象的所有权。该对象值为2048。 p2不再被用来指向该对象。

创建auto_ptr对象时注意的几个问题

(1) auto_ptr的构造函数为explicit,阻止了一般指针隐式类型转换为auto_ptr的构造,所以如下的创建方式是编译不过的。

int* p = new int(1);
auto_ptr<int> ap = p;

如下代码详细解释了关于explicit的作用。

#include <iostream>
using namespace std;

class Test1
{
        public:
                Test1(int i):iValue(i){};
        private:
                int iValue;
                char cValue;
};

class Test2
{
        public:
                explicit Test2(int i):iValue(i){};
        private:
                int iValue;
                char cValue;
};

int main(int argc, char* argv[])
{
        Test1 t1 = 1;//t1.iValue值为1,cValue值为char类型默认值
        Test2 t2 = 2;//编译不过, error: conversion from 'int' to non-scalar type 'Test2' requested
}

(2) 由于auto_ptr对象析构时会删除它所拥有的指针,所以使用时避免多个auto_ptr对象管理同一个指针。如下的使用方法应该避免。

int* np = new int(1);
auto_ptr<int> p1(np);
auto_ptr<int> p2(np);

这样使用会造成p1和p2在析构时都试图删除np,C++标准中多次删除同一个对象会导致未定义的行为。且当p1析构而p2仍然被使用时,会导致空指针访问风险。

(3)auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],所以auto_ptr不能用来管理数组指针。

int *p = new int[100];
auto_ptr<int> ap(p);

如上使用auto_ptr的方式,在ap析构时,执行delete,仅仅释放了数组的第一个元素的空间,仍然会造成内存泄漏,所有使用auto_ptr管理数组不合理的。

(4)C++中对一个空指针NULL执行delete操作是安全的。所以在auto_ptr的析构函数中无须判断它所拥有指针是否为空。

auto_ptr的拷贝构造和赋值

auto_ptr要求对它所拥有的指针完全占有,这一点与引用计数的智能指针不同,也就是说,一个一般指针不能同时被两个auto_ptr所拥有,一方面使用者要避免将用同一个指针构造auto_ptr(3.1(2)的那种方式),另一方面auto_ptr在拷贝构造和赋值运算符重载时要做特殊处理,具体的做法是对所有权进行了完全转移,在拷贝和赋值时,剥夺原auto_ptr对指针的拥有权,赋予当前auto_ptr对指针的拥有权,当前auto_ptr获得auto_ptr的指针,并使原auto_ptr的指针置空,由于会修改原对象,所以auto_ptr的拷贝构造函数以及赋值运算符重重载函数的参数是引用而不是常(const)引用。

这部分需要注意的几个问题

(1) auto_ptr对象被拷贝或者被赋值后,已经失去了对原指针的所有权,此时,对这个auto_ptr的读取操作是不安全的。如下代码是不安全的。

auto_ptr<int> p1(new int(1));
auto_ptr<int> p2(p1);
cout << *p1 << endl;

//and
auto_ptr<int> p3=p1;
cout << *p1 << endl;

这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部的临时auto_ptr对象来接收传入的 auto_ptr(拷贝构造),这样,传入的实参auto_ptr的对其指针的所有权转移到了临时auto_ptr对象上,临时auto_ptr在函数退出时析构,所以当函数调用结束,原实参所指向的对象已经被删除了。

void func(auto_ptr<int> ap)
{
cout << *ap << endl;
}

auto_ptr<int> ap(new int(1));
func(ap);
cout << *ap1 << endl;//错误,函数调用结束后,ap1已经不再拥有任何对象了

因此要避免使用auto_ptr对象作为函数参数按值传递,按引用传递在调用函数是不会发生所有权转移,但是无法预测函数体内的操作,有可能在函数体内进行了所有权的转移,因此按引用传递auto_ptr作为函数参数也是不安全的。使用const 引用传递则可以阻止在函数体内对auto_ptr对象的所有权转移。如果不得不使用auto_ptr对象作为函数参数时,尽量使用const引用传递参数。

(2) auto_ptr支持所拥有的指针类型之间的隐式类型转换。

class base{};
class derived: public base{};
//下列代码就可以通过,实现从auto_ptr<derived>到auto_ptr<base>的隐式转换,因为derived*可以转换成base*类型
auto_ptr<base> apbase = auto_ptr<derived>(new derived);

(3) C++的STL容器对于容器元素类型的要求是有值语义,即可以赋值和复制。auto_ptr在赋值和复制时都进行了特殊操作,所以auto_ptr对象不能作为STL容器元素。

空的auto_ptr 需要初始化吗?

通常的指针在定义的时候若不指向任何对象,我们用Null给其赋值。对于智能指针,因为构造函数有默认值0,我们可以直接定义空的auto_ptr如下:

auto_ptr< int >p_auto_int;  //不指向任何对象

防止两个auto_ptr对象拥有同一个对象(一块内存)

因为auto_ptr的所有权独有,所以下面的代码会造成混乱。

int* p = new int(0);

auto_ptr<int> ap1(p);

auto_ptr<int> ap2(p);

因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr。

警惕智能指针作为参数!

1)按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部auto_ptr删除。如下例:

void f(auto_ptr<int> ap)

{cout<<*ap;}

auto_ptr<int> ap1(new int(0));

f(ap1);

cout<<*ap1;//错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。

2)引用或指针时,不会存在上面的拷贝过程。但我们并不知道在函数中对传入的auto_ptr做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。

结论:const reference是智能指针作为参数传递的底线。

auto_ptr不能初始化为指向非动态内存

原因很简单,delete 表达式会被应用在不是动态分配的指针上这将导致未定义的程序行为。

auto_ptr常用的成员函数

1) get()

返回auto_ptr指向的那个对象的内存地址。如下例:

int* p = new int(33);

cout << "the adress of p:"<< p<< endl;

auto_ptr<int> ap1(p);

cout << "the adress of ap1: "<< &ap1<< endl;

cout << "the adress of the objectwhich ap1 point to: " << ap1.get()<< endl;

输出如下:

the adress of p: 00481E00

the adress of ap1: 0012FF68

the adress of the object which ap1 point to: 00481E00

第一行与第三行相同,都是int所在的那块内存的地址。第二行是ap1这个类对象本身所在内存的地址。

2) reset()

重新设置auto_ptr指向的对象。类似于赋值操作,但赋值操作不允许将一个普通指针指直接赋给auto_ptr,而reset()允许。如下例:

auto_ptr< string > pstr_auto( newstring( "Brontosaurus" ) );

pstr_auto.reset( new string( "Long -neck" ) );

在例子中,重置前pstr_auto拥有"Brontosaurus"字符内存的所有权,这块内存首先会被释放。之后pstr_auto再拥有"Long-neck"字符内存的所有权。

注:reset(0)可以释放对象,销毁内存。

3) release()

返回auto_ptr指向的那个对象的内存地址,并释放对这个对象的所有权。

用此函数初始化auto_ptr时可以避免两个auto_ptr对象拥有同一个对象的情况(与get函数相比)。

例子如下:

auto_ptr< string > pstr_auto( newstring( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2(pstr_auto.get() ); //这是两个auto_ptr拥有同一个对象

auto_ptr< string > pstr_auto2(pstr_auto.release() ); //release可以首先释放所有权

总结:

因为auto_ptr并不是完美无缺的,它的确很方便,但也有缺陷,在使用时要注意避免。首先,不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果

auto_ptr的另一个缺陷是将数组作为auto_ptr的参数: auto_ptr<char>  pstr (new char[12] ); //数组;为定义

然后释放资源的时候不知道到底是利用delete pstr,还是 delete[] pstr;

然后收集了关于auto_ptr的几种注意事项:

1、auto_ptr不能共享所有权。

2、auto_ptr不能指向数组

3、auto_ptr不能作为容器的成员。

4、不能通过赋值操作来初始化auto_ptr

std::auto_ptr<int> p(new int(42));     //OK

std::auto_ptr<int> p = new int(42);    //ERROR

这是因为auto_ptr 的构造函数被定义为了explicit

5、不要把auto_ptr放入容器

C++智能指针--auto_ptr指针

时间: 2024-08-12 13:04:20

C++智能指针--auto_ptr指针的相关文章

智能指针auto_ptr详解

概述:C++中有很多种智能指针,auto_ptr就是其中的一种,该智能指针主要是为了解决"因程序异常退出发生的内存泄漏"这类问题的. 我们先来看下面的问题代码 #include<iostream> #include<memory> #include<exception> using namespace std; //一般指针的处理方式 template<typename T> class OldClass { public: OldCla

C++中的智能指针(auto_ptr)

实际上auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是完全完美的. 本文从下面的8个方面来总结auto_ptr使用的大部分内容. 1. auto_ptr是什么? auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个这样拥有者(auto_ptr).当auto_ptr对象生命

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析,左值与右值

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析 by 小威威 1. 知识引入 在C++编程中,动态分配的内存在使用完毕之后一般都要delete(释放),否则就会造成内存泄漏,导致不必要的后果.虽然大多数初学者都会有这样的意识,但是有些却不以为意.我曾问我的同学关于动态内存的分配与释放,他的回答是:"只要保证new和delete成对出现就行了.如果在构造函数中new(动态分配内存),那么在析构函数中delete(释放)就可以避免内存泄漏了!" 事实果真如此么?

【C++智能指针 auto_ptr】

<More Effective C++>ITEM M9中提到了auto_ptr,说是当异常产生的时候,怎么释放为对象分配的堆内存,避免重复编写内存释放语句. PS:这里书里面提到函数退出问题,函数退出会清理栈内存,不管是怎么正常退出还是异常退出(仅有一种例外就是当你调用 longjmp 时.Longjmp 的这个缺点是 C++率先支持异常处理的主要原因).建立在此基础上我们才把对指针的删除操作封装到一个栈对象里面.这样函数退出(异常或是正常)就会调用对象的析构函数,达到我们自动清理所封装指针指

C++智能指针 auto_ptr

C++智能指针 auto_ptr auto_ptr 是一个轻量级的智能指针, 定义于 memory (非memory.h)中, 命名空间为 std. auto_ptr 适合用来管理生命周期比较短或者不会被远距离传递的动态对象, 最好是局限于某个函数内部或者是某个类的内部. 使用方法: std::auto_ptr<int> pt(new int(10)); pt.reset(new int(11)); 成员函数 3个重要的函数: (1) get 获得内部对象的指针, 由于已经重载了()方法, 因

智能指针auto_ptr

前奏: Garbage Collection 技术一直颇受注目,并且在 Java 中已经发展成熟,成为内存管理的一大利器,但它在 C++ 语言中的发展却不顺利,C++ 为了追求运行速度,20 年来态度坚决地将其排除在标准之外. 为了稍许平复因为没有 Garbage Collection 而引发的 C++ 程序员的怨气,C++对 Smart Pointer 技术采取了不同的态度. 首先,了解一下智能指针, 该方法使用一个指针类来代表对资源的管理逻辑,并将指向资源的句柄(指针或引用)通过构造函数传递

stl智能指针auto_ptr

感觉auto_ptr还是存在一些不足的,效率十分低,而且丢失了一般指针方便常用的操作,如++,--等,auto_ptr只重载了=,*,->这几个操作符,所以使用很不方便. 一.说明:int *a = NULL;delete a;//do nothing 应此auto_ptr的析构函数是这样子的: ~auto_ptr() { // destroy the object delete _Myptr; }    二.auto_ptr中有一个很重要的函数release _Ty *release() _T

C++ 智能指针auto_ptr详解

1. auto_ptr 的设计动机: 函数操作经常依照下列模式进行: 获取一些资源 执行一些动作 释放所获取的资源 那么面对这些资源的释放问题就会出现下面的两种情况: 一开始获得的资源被绑定于局部对象,那么当函数退出的时候,这些局部对象的析构函数被自动的调用,从而自动释放掉这些资源; 一开始获得的资源是通过某种显示手段获取,而且并没有绑定在任何对象身上,那么必须以显式的方式释放.这种情况常常发生在指针身上; 例子: 1 void f() 2 { 3 ClassA* ptr = new Class

【C++】智能指针auto_ptr的简单实现

//[C++]智能指针auto_ptr的简单实现 #include <iostream> using namespace std; template <class _Ty> class auto_ptr { public: auto_ptr(_Ty *_P = 0) :_Owns(_Ptr != 0), _Ptr(_P) {} auto_ptr<_Ty>(const auto_ptr <_Ty> &p):_Owns(p._Owns),_Ptr(p.r