- 论坛徽章:
- 0
|
本帖最后由 幻の上帝 于 2011-11-08 21:16 编辑
回复 42# 狗蛋
>>当我说“苹果怎么削”时,当然和“橘子怎么剥”狗屁关系没有。
然而你说的都是或剥或削的问题。那么干嘛需要明确是苹果还是土豆呢。
>>换言之,我说”new要和delete一一对应“时,当然指的就是new/delete关键字本身,和placement new没关系。
意思我还是清楚,但这你又错了。关键字本身不考虑具体用法对资源管理来说是不合时宜的,你不得不明确你是在怎么new,然后才能谈你的形式上的对称。你说的是new expression/delete expression,然而placement也有两重含义:new expression语法上的placement(也就是紧跟在new后面的一堆(!@$#)),还有就是operator new(sz, !@#$)以及operator delete(p, !@#$)。说道某种具体的实现方式,你可以拒绝使用placement,但是泛泛地谈new的时候你还是回避不了。
>>再次,资源申请和释放形式要对称,这是为了让资源管理方案清晰、便于排错。
这点我很同意。不过,有两个绝对不可能对称的地方。
第一,就是我在41L举的例子,绑定到自动对象上的RAII。不过,隐式析构体现的语义和使用的技巧和微软式的封装毫无关系。而自动对象本身来源于C。
第二,注意我刚才说的。new expression有placement,但delete expression没有这种选项。偏偏operator delete是有placement的,用于对应形式的new expression抛出异常(注意默认 :: operator new的placement不抛异常,这里是初始化对象时抛的异常)的处理。这种不对称是既定的语言特性。
关于第一种情形的显然的不对称的话题,等会再讲。
>>当然,万能的C++当然也可以模拟垃圾回收。此时delete自然就不应该出现——但这和“手动资源管理”有蛋关系?!
垃圾回收和“手动资源管理”是没关系,不过,什么情况下算是“手动”,有待商榷。
垃圾回收之所以不算手动,是因为对象所有权扔给垃圾回收器以后可以不管什么时候会被析构了——而不只是可以不写delete。所谓不具有确定性的析构。
但是,上面的RAII不一样。在块作用域中写下std::shared_ptr<int> ptr(new int);这样的代码时,用户需要明明白白地清楚这里new出来的东西将会在ptr的作用域结束时析构,而不仅仅是“不用写delete”。而这里不写delete的原因是编译器对于自动对象产生隐式析构的代码,给重用(这里是std::default_delete)提供了可能。这的确可以是福利。但解放了你的手不等于你可以不用思考。这里不能排除“手动”的成分,意即用户必须明确什么时候析构,乃至明确整个对象的生存期。
关于这里显然的不对称的话题,仍然等会再讲。
>>最后,有new无delete的设计正常吗?提供了什么便利?其它多少语言都做到了无new无delete(同样有垃圾收集功能!),它们和java相比,在相关方面有什么劣势?或者说,这种有new无delete的设计,是某个功能的前提、还是抽象上的残疾,或者干脆是扯淡?
对称性的问题放到这里来讲。
先说刚才提到的第二种情形,关于为什么没有delete placement expression。我记得BS的主页上有提到过,既然已经有了显式析构函数调用,那就不需要了。这样的设计或许还真算是残疾——至少不能算优雅。
然后是RAII。关键在于,为什么释放资源能够使用隐式的代码。原因很简单:析构函数和构造函数不可能保证对称。其实上面的情形也多少受到这个影响。
不过,真正麻烦的原因不在这里。你说有些语言可以无new无delete——无delete是容易设计得出来(可以扔给运行时库然后用户什么都不用管了),但即使没有new,也存在替代品,包括类似C的自动对象的玩意儿——除非那种语言不用显式声明和定义使用存储的实体(在类C语言里就是对象)并据此确定它的生存期起始,或者不这样做但是能对用户提供特性取消声明/定义的作用同时在其上附加隐式终止生存期的语义。类C语言看来是没法突破这个框框的。
在扯远点还有很多东西证明不对称的普遍性。T-SQL里面可以drop掉一个函数,类C语言即使没有声明定义的规则的限制,从实现上来说也是没办法的,因为这里的函数是编译时静态地确定的。拿构造→使用成员→析构类比,编译→运行→??(反编译?)显然不太通。(……跑题太远我自重。 ) |
|