C++ 智能指针(一)

  •   内存安全  

  在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象来进行初始化;delete,接收一个动态对象的指针,销毁该对象,并释放与之关联的内存。

  动态内存的使用很容易出问题,因为确保在正确的时间释放内存是及其困难的。有时我们会忘记释放内存(或程序抛出异常),在这种情况下就会产生内存泄漏;有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针(段错误)

  下面写个Demo测试程序  

#include <memory>
#include <exception>
#define  FLAG 3    //用于编译不同的程序

class Demo1
{
public:
	Demo1()
	{
		std::cout << "Demo1" << std::endl;
	}

	~Demo1()
	{
		std::cout << "~Demo1" << std::endl;
	}
};

bool throw_test(bool flag)
{
	if (flag)
	{
		throw "throw_test";
	}

	return flag;
}

int main(int argc, char* argv[])
{
	Demo1 *pDemo1 = new Demo1();

	#if (FLAG == 1)
	throw_test(true);  //1.执行这条语句,会打印~Demo1?
	#endif

	#if (FLAG == 2)
	try
	{
		throw_test(true);
	}
	catch (...)	//2....代表捕获所有异常
	{
		delete pDemo1;	//3.执行这条语句,会打印~Demo1?
		throw;
	}
	#endif

	#if (FLAG == 3)
	delete pDemo1;
	#endif

	return 0;
}

  上述程序结果如下:

   当宏FLAG为1时,执行throw_test(true),即使程序抛出异常,它没有打印~Demo1;

  当宏FLAG为2时,执行throw_test(true),程序会抛出异常,之后捕获到异常打印~Demo1;

  当宏FLAG为3时,执行delete pDemo1,这是正常操作,程序会调用Demo1的析构函数,打印~Demo1;

  •  智能指针

  C++中的智能指针类型有:auto_ptr,shared_ptr,unique_ptr,weak_ptr(后三者为C++11新增的),它们均为类模板,使用需要包含<memory>头文件

  常规指针带来的风险

Demo1* pDemo1 = new Demo1();
Demo1* pDemo2;
pDemo2 = pDemo1;
printf("pDemo1:%x pDemo2:%x\n", pDemo1, pDemo2); 

  上面的pDemo1和pDemo2是常规指针,指向同一个对象(浅拷贝),因此打印的地址也是一样的;请试想一下如果其中一个指针执行了delete操作,那么另一个指针再执行别的操作会怎样?程序会发生段错误。要避免这个问题,可以用下面这些方案:

  1. 定义赋值运算符函数,进行深拷贝,这样的操作会使上面的两个指针不再指向同一个对象,缺点是浪费空间,所以智能指针都未采用此方案
  2. "独占"所指的对象;对于特定的对象,某一时刻只能有一个指针指定一个给定对象,当指针被销毁时,它所指的对象也被销毁,这就是用于auto_ptr和uniqiie_ptr 的策略,但unique_ptr的策略更严格。
  3. 利用引用计数,创建记录型的指针;例如,赋值时,计数将加1,而指针过期时,计数将减1。当减为0时才调用delete。这是shared_ptr采用的策略。

 

  在C++11中,auto_ptr已弃用;编写一段测试程序,Demo1类还是使用上面定义的。

int main(int argc, char *argv[])
{
	std::auto_ptr<Demo1> pDemo1(new Demo1);
	std::auto_ptr<Demo1> pDemo2;
	pDemo2 = pDemo1;
	printf("pDemo1:%p pDemo2:%p\n", pDemo1, pDemo2);  //运行到这里pDemo1会打印什么?
        return 0;
}    

  上面的程序运行后,打印pDemo1的地址为NULL。具体可以查看下auto_ptr的赋值运算符的实现是如何的。

  下面这两张截图是VS2015下的auto_ptr的赋值运算符的实现;我们可以看到使用赋值运算符时,会调用reset函数,这是会将_Myptr的内存delete,并将地址置为NULL;如果pDemo1调用成员函数,此时会发送什么?

    

 

 

   

  

  auto_ptr存在内存崩溃的风险,这个或许就是auto_ptr被C++11弃用的原因吧。

  未完待续...

猜你喜欢

转载自www.cnblogs.com/coder-zyc/p/9521260.html