C++的最佳特性(译) (二)

2014-11-23 22:04:20 来源: 作者: 浏览: 4
8.1的用户可能不会注意这个)同样的事情(传递地)发生在std::vector上。move操作像是仿真了另一个唯一的对象的生命周期。在这个过程中,我们有资源(文件句柄的集合)的“虚拟的”生命周期和“手工的”生命周期,其中这个生命周期从ans被创建开始,到定义在另外一个作用域里的不同的对象生命周期结束而结束。

注意到,整个文件句柄的集合整个的“扩展的”生命周期中,如果有异常发生,每个句柄都被保护不会泄露。即使name函数在第5次迭代时发生了异常,之前已经创建好的4个元素都会保证在produce函数被正确析构。

类似的,你无法通过“保护”语句做到做到下面的效果:


class CombinedResource
{
std::fstream f;
Socket s;

CombinedResource(string name, unsigned port)
: f{name}, s{port} {}
// 没有显式地调用析构函数
};这段代码已经给了你好几个有用的安全性保障。两个资源会在CombinedResource的生命周期结束时被释放:这是在隐式的析构函数中按照与初始化的相反的顺序来处理的,你不需要去手工写这些代码。假设在初始化第二个资源s的时候,在其构造函数中发生了异常,已经被初始化好了的f的析构函数会被立即调用,这个过程在异常从s的构造函数中向上抛出时已经完成了。你可以免费获取到这些安全性保障。

试问,你怎么通过using或者try来保证上面的安全性保障呢?

不好的方面
这边有必要提一些有些人不喜欢析构函数的原因。在某些情况下,垃圾回收比C++提供的资源管理方式要好。比如,有了垃圾回收器(如果你用得起它的话),你可以仅仅通过分配节点然后通过指针(你也可以称之为“引用”)把他们连接起来,来很好地表示一个带环的图。在C++中,你没法做到这一点,甚至用“智能”指针也不行。当然,这种通过垃圾回收来管理的图中的节点没法管理资源,因为它们可能会泄露:using或者try语句在这里不起作用,因为finalizer函数不一定会被调用。

还有,我听一些人说有一些高效的并行算法只能在垃圾回收器的帮助下完成。我承认我没见过这样的算法。

有些人不喜欢在看不到代码中看不到析构函数,有些人喜欢这种方式,也有人不喜欢。当你在分析和调试程序时,你可能不会注意某个析构函数被调用了,而且这可能有一些副作用。我在调试一个大而混乱的程序时,就曾经落入这个陷阱中。一个被野指针(raw pointer)指向的对象可能突然会因为某个未知的原因变得无效了,而且我也看不出来有什么函数会导致这种情况。后来我才意识到同一个对象被另一个unique_ptr所指向,而这个unique_ptr又悄无声息地超出了作用域。对于临时对象来说,情况可能会更糟,你既看不到析构函数,也看不到对象本身。

在使用析构函数的时候有一些限制:为了能够使析构函数与栈展开(stack unwinding,由异常导致,是C++中异常处理的标准流程)正确地协作,它们本身不能抛出异常。这个限制对于某些人来说非常难,因为他们需要来标志资源释放失败了,或者用析构函数达到其它的目的。

注意,在C++中,除非你将析构函数定义为noexcept(false),那么它会被隐式地声明为noexcept,如果异常异常从其中抛出的话,就会调用std::terminate。加入你想在发生异常的时候标志资源释放失败,推荐的做法是提供一个像release这样的成员函数用来显示调用,然后让析构函数检查资源是否已释放,如果没有,则“安静地”释放(吞下任何异常而不继续抛出)。

这种通过析构函数来释放资源的另一个潜在的弊端是,有些时候你需要在你的函数中引入额外的手工的作用域(或者叫块),而这仅仅是为了在函数的作用域结束之前触发局部对象的析构函数。比如:


void Type::fun()
{
doSomeProcessing1();
{
std::lock_guard g{mutex_};
read(sharedData_);
}
doSomeProcessing2();
}这里,我们不得不加入一个额外的程序块,保证我们再调用doSomeProcessing2函数的时候mutex没有被锁住:我们想在停止使用资源后立即释放它们。这个看上去就有点像using或try语句了,但是有两个区别:

1. 这是一种例外,而不是以一种规则;

2. 如果我们忘了这个作用域,资源会被持有更长的时间,但不会泄露,因为它的析构函数绑定在调用者身上。

这就是我要讲的。我个人感觉析构函数是所有程序语言里面最优雅和实用的特性,而且我还没提到其它的优势:和异常处理机制的相互作用。这是C++中比性能更能吸引我的特点:优雅。

-->

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: