c++智能指针(2) -- auto_ptr

概述

auto_ptr是c++98提出的智能指针,其可以帮助我们管理动态分配的空间。但是,注意auto_ptr存在一定的问题(后面会说),c++11提出的unique_ptr已经完全取代了auto_ptr所以在可以使用unique_ptr的情况下,建议使用unique_ptr。

使用auto_ptr需要导入头文件 #include <memory>

1.  定义auto_ptr对象

  • auto_ptr<类型> 名称(new 空间),// 类型:  智能指针指向的类型,空间:智能指针管理的空间
  • auto_ptr<int> p1(new int());  // 创建一个p1的智能指针对象,其指向int类型的数据,动态开辟一个int类型的空间作为参数(其内部的指针会指向这片空间) 
  • auto_ptr<int> p1;  // 创建一个空对象,指向int类型的数据,其内部的指针会指向NULL

2. 使用auto_ptr 

其实使用智能指针和普通指针的方法是一样的,对于普通指针最常用的就是*和->,对于智能指针也同样是适用的,因为其内部对这些操作符进行了重载。

auto_ptr<int> p1(new int(1));  // 只能指针p1指向的空间中存放的值为1。
cout << *p1 << endl; // 我们同样对p1进行*解引用就可以取到内部存放的值。

class _Ptr {
public:
    _Ptr() {
        nub = 10;
        cout << __FUNCTION__ << endl;
    }

    ~_Ptr() {
        cout << __FUNCTION__ << endl;
    }

    int getNub()const {
        return nub;
    }

private:
    int nub;
};

void main() {
    auto_ptr<_Ptr> p1(new _Ptr());

    cout << p1->getNub() << endl;
    cout << (*p1).getNub() << endl;;
}

上面代码中定义了一个类,我们使用智能指针p1指向这个类的对象。我们想要访问其内部的成员方法可以使用->直接访问,也可以解引之后用.访问,和普通指针是一样的。

3.auto_ptr中的函数

1)get()函数 

  • _Ptr* get();  // 这个函数返回一个指向当前智能指针所管理内存的指针。

    我们需要使用一个此类型的指针接收这片空间,当然也可以不用,但是注意如果接收之后,智能指针释放了这块内存,我们就不能使用指针访问了。


例:   auto_ptr<int> p1(new int());   // 指向int的智能指针

        int* i = p1.get();  // 调用get()函数,返回一个指向上面动态开辟的空间的指针。

2) reset()函数

  • 这是一个重设函数,就是重新设置当前的智能指针,传入一个空间的地址,如果这个地址和之前指向的地址不一样,那么reset函数就会让智能指针指向传入的内存,释放之前指向的空间。(当然如果传入的地址空间还一样,那自然不需要重设)
  • void reset(ptr);   // 如果传入的地址和之前指向的地址不同,那么就释放之前的地址,指向新传入的地址。
  • void reset();  // 什么也不传入,就是释放之前指向的空间,然后指向null。


所以,reset()函数不仅可以用来重新设置智能指针的指向,还可以将智能指针置空。

例: 

auto_ptr<int> p1(new int);

p1.reset(); // 将指针置空

p1.reset(new int()); // 指向另外一片空间

3)release()函数 

我们说过,智能指针的作用就是帮助我们管理动态分配的空间,但是如果我们不希望它们管理了,想要自己管理,这时候就需要使用release()函数了。

这个函数的作用就是收回智能指针对动态空间的管理权。

  • _Ptr* release();   // 此函数会返回智能指针指向的空间,然后将自己的指针置为null。也就是,智能指针不再指向这个空间,不再管理它了。

    我们需要用一个同类型的指针接收这块内存,接过管理权,但是记住,如果这样这块内存就得你自己手动释放了。(注意: 如果你接收,就造成内存泄露了)


例: 

auto_ptr<int> p1(new int);

int* p2 = p1.release();  // 使用p2接收其返回的指针,我们自己管理这片空间。

4)赋值函数 

就是赋值运算符的重载函数,对于auto_ptr的赋值重载函数需要注意。
假设存在两个auto_ptr对象,p1和p2。
p1 = p2 ;  // 会调用p1的赋值运算符重载函数,进行赋值

上面的过程很好理解,但是这并不和普通指针的赋值一样,对于普通指针p1和p2最终会直系那个同一片内存,但是对于auto_ptr不同。

它会先将p2指向null(调用p2的release()函数),如果p2和p1指向的空间不一样,就释放掉p1的空间,让其指向p2的空间(调用p1的reset()函数)。

其实释放掉p1的空间是正常,因为,对于普通指针,如果p1指向一片空间,p2也指向一个,直接p1=p2,会导致p1对应的空间发生泄露,所以赋值之前应该delete p1。只不过是智能指针做了这个事。


但是,我们需要注意的是,p2指向了null,这和我们平时使用指针是不一样的。

4. auto_ptr的问题 

auto_ptr存在一些问题,这导致它在c++11被抛弃,被unique_ptr取代,所以能用unique_ptr的地方就使用unique_ptr。 

问题一: 

  • 当两个智能指针管理同一片空间(拷贝构造函数),如果一个析构会影响另外一个


下面的代码就会出现这样的问题 

#include <iostream>
#include <stdlib.h>
#include <memory>

using namespace std;

class _Ptr {
public:
	_Ptr() {
		nub = 10;
		cout << __FUNCTION__ << endl;
	}

	~_Ptr() {
		cout << __FUNCTION__ << endl;
	}

	int getNub()const {
		return nub;
	}

private:
	int nub;
};

int main(void) {
	auto_ptr<_Ptr> p1(new _Ptr());
	{
		auto_ptr<_Ptr> p2(p1);
	}
	p1->getNub();

	system("pause");
	return 0;
}

在main函数中,我们定义一个智能指针p1指向了一片空间,然后我们在{}内部重新定义了一个智能指针另其指向和p1的同一片内存。

当{}的作用域结束,p2的生命周期就结束l(局部变量的声明周期就是{开始,}结束),调用其析构函数析构对象,同时释放了其管理的空间。

但是p1和p2管理同一片空间,在后面代码中,我们还希望访问p1中的数据,可是空间已经被p2释放了,也就是此时p1并没有指向空间了。我们还去访问自然就会出错了。

执行,程序会终端,提示: auto_ptr not dereferencable; 

问题二: 

就是当我们使用容器存储智能指针对象的时候,是不符合容器的规则的,比如容器允许赋值。 

看下面的代码

#include <iostream>
#include <stdlib.h>
#include <memory>
#include <vector>

using namespace std;

int main(void) {
	vector<auto_ptr<int>> v1;

	auto_ptr<int> p1(new int(1));
	auto_ptr<int> p2(new int(1));

	/*
	 这样写是不对的,我们需要使用move()函数将对象移动成右值才能成功插入
	 v1.push_back(p1);
	 v1.push_back(p2);
	*/
	v1.push_back(std::move(p1));
	v1.push_back(std::move(p2));

	// 可以访问
	cout << *v1[0] << endl;
	cout << *v1[1] << endl;

	v1[0] = v1[1];

	// 错误
	cout << *v1[0] << endl;
	cout << *v1[1] << endl;

	system("pause");
	return 0;
}

上面代码中,我们使用vector容器存放智能指针对象,(插入的时候需要使用move()函数现将对象移动成右值才行,否则会出问题)  然后去访问每个位置智能指针的值,第一次访问都是正常的。

但是当我们将vector中的一个元素赋值给另外一个元素的时候,再输出就会出错。

原因就是问题一造成的,两元素赋值的时候,将后面元素置为null了,当然访问不了其内部元素。但是,对于vector容器的特性,是允许这样操作的,所以如果存放auto_ptr的对象,这样是不被允许的。

为什么在插入数据的时候需要使用move()函数?

原因主要是因为,auto_ptr的构造函数使用关键字 explicit 限定的,这就说明我们在auto_ptr类创建对象的时候只能显示创建,不能隐式创建。  --  具体看关键字explicit的讲解
auto_ptr<int> p1(new int());  // 显示创建

auto_ptr<int> p1 = auto_ptr<int>(new int()); // 隐式创建

而vector中的push_back()只有两种重载函数,一种参数为const auto_ptr<int>&

                                                                        另一种为auto_ptr<int>&& (右值引用)


我们之前说到过,const的引用是会开辟空间的,和普通的引用不同,所以将p1作为参数传入,就相当于 auto_ptr<int> p1 = auto_ptr<int>(new int()); 是这种情况,但是auto_ptr是不允许这样的,所以是错误的,  --   注意对于普通的引用还是取别名,不会再开辟空间,但是const的引用会再开辟空间,将对应的数据拷贝一份,就相当于创建对象了。

这样我们只能使用另外一个重载,它接受一个右值,左值是不行的,但是p1是一个左值,所以我们需要使用move()函数将它变为右值才行。

问题三: 

问题三就是auto_ptr不能指向数组类型 -- 但是unique_ptr是可以的。

比如:  auto_ptr<int []> p1(new int[5]); // 是错误的。 

相关推荐

  1. c++智能指针2) -- auto_ptr

    2024-04-15 00:34:01       17 阅读
  2. C++智能指针2——unique_ptr和weak_ptr

    2024-04-15 00:34:01       13 阅读
  3. C++ 智能指针

    2024-04-15 00:34:01       29 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-15 00:34:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-15 00:34:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-15 00:34:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-15 00:34:01       18 阅读

热门阅读

  1. Elasticsearch 支持的插件 —— 筑梦之路

    2024-04-15 00:34:01       17 阅读
  2. 顺序表菜单栏的实现

    2024-04-15 00:34:01       13 阅读
  3. 漫谈:C语言 值传递 函数参数 指针

    2024-04-15 00:34:01       15 阅读
  4. python_day27

    2024-04-15 00:34:01       14 阅读
  5. 如何做一个自己的开源项目

    2024-04-15 00:34:01       14 阅读
  6. Qt中显示hex数据的控件

    2024-04-15 00:34:01       13 阅读
  7. C++:运算符与表达式 (信奥赛练习)

    2024-04-15 00:34:01       15 阅读
  8. LeetCode 61. 旋转链表

    2024-04-15 00:34:01       11 阅读
  9. Python装饰器

    2024-04-15 00:34:01       14 阅读
  10. Vue EasyUI插件 学习笔记(基础)详细版

    2024-04-15 00:34:01       17 阅读