智能指针模板类

  智能指针是行为类似于指针的类对象,但这种对象还有其他功能。

void remodel(std::string & str)
{
	std::string * ps = new std::string(str);
	...
	if (weird_thing())
		throw exception();
	str = *ps;
	delete ps;
	return;
}

当出现异常时,delete将不被执行,将导致内存泄漏。
  当函数终止时(不管是正常终止,还是异常终止),本地变量都将从栈内存中删除——因此指针ps占据的内存将被释放。但是ps指向的内存没有被释放。如果ps有一个析构函数,该析构函数将在ps过期时释放它指向的内存。ps的问题在于它只是一个常规指针,不是有析构函数的类对象。这正是auto_ptr, unique_ptr和shared_ptr背后的思想。模板auto_ptr是C++98提供的解决方案,c++11已将其摒弃,并提供了另外两种解决方案。

使用智能指针
  要创建智能指针对象,必须包含头文件memory,然后使用通常的模板语法来实例化所需类型的指针。

template<class X> 
class auto_ptr {
public:
	explicit auto_ptr(X * p = 0) throw();
	...
}

创建智能指针对象

auto_ptr<double> pd(new double);
unique_ptr<double> pdu(new double);
shared_ptr<string> pss(new string);

所有智能指针类都有一个explicit构造函数,该构造函数将指针作为参数,禁止自动将指针转换为智能指针对象。

shared_ptr<double> pd;
double *p_reg = new double;
pd = p_reg;//不可行的
pd = shared_ptr<double>(p_reg);
shared_ptr<double> pshared = p_reg;//不可行的
shared_ptr<double> pshared(p_reg);

  智能指针对象的很多方面都类似于常规指针。例如可以对它执行解除引用操作(* ps)、用它来访问结构成员(ps->name)、将它赋给指向相同类型的常规指针。还可以将智能指针对象赋给另一个同类型的智能指针对象。

有关智能指针的注意事项

auto_ptr<string> ps (new string("I reigned lonely as a cloud."));
auto_ptr<string> vocation;
vocation = ps;

  如果ps和vocation是常规指针,则两个指针指向同一个string对象。这是不能接受的,因为程序将试图删除同一个对象两次——一次是ps过期时,另一次是vocation过期时。要避免这种问题,方法有以下几种:
• 定义赋值运算符,使之执行深复制,这样两个指针指向不同的对象,其中的一个对象是另一个对象的副本;
• 建立所有权概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的构造函数会删除对象。然后,让赋值操作转让所有权,这就是用于auto_ptr和unique_ptr的策略,但unique_ptr的策略更严格;
• 创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用delete。这是shared_ptr采用的策略。

int main()
{
	using namespace std;
	auto_ptr<string> films[5] = 
	{
		auto_ptr<string> (new string("Fowl Balls")),
		auto_ptr<string> (new string("Duck Walks")),
		auto_ptr<string> (new string("Chicken Runs")),
		auto_ptr<string> (new string("Turkey Errors")),
		auto_ptr<string> (new string("Goose Eggs")),
	};
	auto_ptr<string> pwin;
	pwin = films[2];
	for (int i = 0; i < 5; i++)
	{
		cout << *films[i] << endl;
	}
	return 0;
}

  上述代码在执行i = 2的for循环时将会出错,这是因为代码pwin = films[2];将所有权转让给了pwin,films[2]自身将是一个空指针。如果上述程序使用shared_ptr代替auto_ptr,则程序将正常运行。如果使用unique_ptr,程序不会等到运行阶段崩溃,而在编译阶段报错。因此unique_ptr比auto_ptr更安全(编译阶段错误比潜在的程序崩溃更安全)

有一种情况下,将一个智能指针赋给另一个并不会留下危险的悬崖指针。

unique_ptr<string> demo(const char * s)
{
	unique_ptr<string> temp(new string(s));
	return temp;
}

unique_ptr<string> ps;
ps = demo("Uniquely special");

  demo()返回一个临时的unique_ptr,然后ps接管了原本归返回的unique_ptr所有的对象。总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这样做,如果源unique_ptr存在一段时间,编译器将禁止这样做。
相比于auto_ptr,unique_ptr还有一个优点,它有一个可用于数组的变体。

std::unique_ptr<double []>pda(new double(5));

使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new[]分配内存时,不能使用它们。不使用new分配内存时,不能使用auto_ptr或shared_ptr;不使用new或new[]分配内存时,不能使用unique_ptr。

选择智能指针
  如果程序要使用多个指向同一个对象的指针,应选择shared_ptr,这样的情况包括:
1.有一个指针数组,并使用一些辅助指针来标识特定的元素,如最大的元素和最小的元素;
2.两个对象包含都指向第三个对象的指针;
3.STL容器包含的指针;
  很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr和auto_ptr。如果您的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。

  如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。如果函数使用 new分配内存,并返回指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。

  在unique_ptr为右值时,可将其赋给shared_ptr。模板shared_ptr包含一个显式构造函数,可用于将右值unique_ptr转换为shared_ptr。

//make_int返回一个unique_ptr<int>
unique_ptr<int> pup(make_int(rand() % 1000));//ok
shared_ptr<int> spp(pup);//not allowed
shared_ptr<int> spr(make_int(rand() % 1000));//ok
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基类智能指针转子类智能指针是一种在面向对象编程中常见的操作,用于将指向基类对象的智能指针转换为指向子类对象的智能指针。这种转换通常发生在需要使用子类特有的方法或属性时。 在C++中,可以使用dynamic_pointer_cast函数来进行基类智能指针到子类智能指针的转换。dynamic_pointer_cast是一个模板函数,它接受两个参数:要转换的目标类型和要转换的智能指针。 以下是一个示例代码: ```cpp #include <iostream> #include <memory> class Base { public: virtual void foo() { std::cout << "Base::foo()" << std::endl; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo()" << std::endl; } void bar() { std::cout << "Derived::bar()" << std::endl; } }; int main() { std::shared_ptr<Base> basePtr = std::make_shared<Derived>(); std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr); if (derivedPtr) { derivedPtr->foo(); derivedPtr->bar(); } else { std::cout << "Failed to cast basePtr to derivedPtr" << std::endl; } return 0; } ``` 在上述示例中,我们创建了一个基类Base和一个派生类Derived。然后,我们使用std::make_shared函数创建了一个指向Derived对象的基类智能指针basePtr。接下来,我们使用dynamic_pointer_cast将basePtr转换为指向Derived对象的智能指针derivedPtr。如果转换成功,我们就可以使用derivedPtr来调用Derived类中的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值