《Effective C++》资源管理:条款26

条款26:尽可能延后变量定义式的出现时间

在程序中定义一个变量,当控制流(control flow)到达这个变量定义时,程序就要承受变量的构造成本,当控制流离开这个作用域时,程序也要承受析构成本。无论这个变量是否使用,都要承受这些成本。应该尽量避免这种情形。或许你认为自己不会这样使用,但也未必。例如要写一个加密函数,但加密的密码要足够长。如果密码太短,会抛出一个异常logic_error(c++标准库,**条款**54)。

std::string encryptPassword(const std::string& psaaword){using namespace std;string encrypted;if(password.length()<MinimumPasswordLength){throw logic_error(“Password is too short”);}……//加密密码,把加密结果放到encrypted内return encrypted;}

如果这个函数抛出异常,那么变量encrypted即便是未使用,也会执行构造函数和析构函数。 所以上述函数应该推迟encrypted的定义,直至到真正需要它 std::string encryptPassword(const std::string& psaaword) { using namespace std;

if(password.length()<MinimumPasswordLength){throw logic_error(“Password is too short”);}string encrypted;……//加密密码,把加密结果放到encrypted内return encrypted;}

但这段代码任何不够高效。使用变量encrypted时,先定义,之后再给它赋值。在**条款**4曾经提到过,通过default构造函数构造一个对象,然后再用赋值操作符赋值,其效率不如使用一个直接指定初值的构造函数。

std::string encryptPassword(const std::string& psaaword){using namespace std;if(password.length()<MinimumPasswordLength){throw logic_error(“Password is too short”);}string encrypted;//定义encrypted=password;//赋值encrypt(encrpted);……//加密密码,把加密结果放到encrypted内return encrypted;}

其效率不如

std::string encryptPassword(const std::string& psaaword){using namespace std;if(password.length()<MinimumPasswordLength){throw logic_error(“Password is too short”);}string encrypted(password);//定义+赋值encrypt(encrpted);……//加密密码,把加密结果放到encrypted内return encrypted;}

这个条款“尽可能延后”的意义,不仅仅是把变量定义延迟到变量的使用前一刻为止,甚至该尝试延后这份定义到能够给它赋初值为止。这样还可以避免无意义的default构造函数。

那么循环时怎么做呢?把变量定义在循环外还是循环内?

代码A

Widget w;//定义在循环外for(int i=0;i < n;++i)w=……;……}

代码B

for(int i=0;i<n;++i){Widget w(……);//定义并赋值……}

代价 代码A;1个构造函数+1个析构函数+n个赋值操作

代码B:n个构造函数+n个析构函数

如果class赋值成本低于构造成本+析构成本,代码A高效。否则代码B高效。但是代码A中,w的作用域比较代码B大,这样做可能给程序的可理解性和已维护性造成冲突,所以除非(1)你知道赋值成本比“构造+析构”成本低,,(2)你正在处理代码中效率高度敏感。否则应该使用代码B

总结尽可能延后变量定义式的出现。这样可以增加程序清晰性并改善效率。

条款27尽量少做转型动作

C++规则的设计目标之一是保证“类型错误”不发生。如果编译时出现与类型转换相关的警告,别轻易放弃它,要确保这个警告是否安全。 C++中的转型(casts)会破坏类型系统(type system)。C++不同于C、Java和C#,这些语言中的转型比较必要,且难以避免,也比较不危险。 转型语法以后下三种 C风格的转型

(T)expression

函数风格的转型

T(expression)

上面两种形式无差别,只是小括号的位置不同而已。 C++中提供四种新式转型

const_cast<T>(expression)dynamic_cast<T>(expression)reinterpret_cast<T>(expression)static_cast<T>(expression)

这四种各有不同用法 **1.**const_cast通常被用来将对象的常量特性转除(cast away the constness)。它也是唯一由此能力的C++-style转型操作符。 **2.**dynamic_cast主要用来执行“安全向下转型”(safe downcasting),也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作(后面细讲)。 **3.**reinterpret_cast意图执行低级转型,实际动作(结果)可能 取决于编译器,这表明其不可移植。例如将pointer to int转为int,这类转型常用在低级代码。例如,讨论讨论如何针对原始内存(raw memory)写一个调试用的分配器(debugging allocator),见条款50. **4.**static_cast执行强迫隐式转换(implicit conversions)。例如将int转为double,non-const转为constant等。它也可以用来执行一些转换的反响转换,但无法将const转为non-const。

为了兼容,旧式的转型仍然合法,但是更提倡用新式的形式。因为1、新式转型很容易被辨识出来,可以很快找到代码中有哪些转型。2、新式转型动作的目标愈窄化,编译器愈可能诊断出错误的运用。

但是当条用explicit构造函数将一个对象传给函数时, 常常使用旧式转型:

class Widget{public:explicit Widget(int size);……};void doSomeWord(const Widget& w);doSomeWork(Widget(15));//函数风格doSomeWork(static_cast<Widget>(15));//C++风格

使用C++风格转型时,不怎么像生成对象,而函数风格看起来更自然些。但是这只是感觉,只是看起来而已,这可能到时core dump。 转型在表面看起来是把一种类型视为另一种类型,但是编译器做的远远比这个多。编译器在编译器期间会真的编译出运行期间的代码。例如下面代码:

int x,y;……double d=static_cast<double>(x)/y;

将x转型为double,和不转型对比,代码肯定不同。因为double和int的底层表示都不相同。下面这个例子更加明显

class Base{……};class Derived:public Base{……};Derived d;Base* b=&d;//隐喻的将Derived*转为Base*

上面代码是实现多态的手段之一,经常看到。Base对象和Derived对象的大小并不相同。这时,通常有个偏移量(offset)在运行期间施与Derived*指针身上,用于取得正确的Base*指针。 上面也说明单一对象,可能有多个地址,例如Derived对象,Derived*指针指向它和Base*指针指向它。在继承中经常发生这样事。通常你不应该假设对象在内存中如何布局,更不该以此假设为基础进行转型。例如把Base*转型为char*进行字符操作。 但是有时候我们需要一个偏移量,知道对象的布局,并以此进行了转型。但是编译器不同可能会导致对象布局不同,这意味着在一个平台编译通过的代码,在另一个平台未必能行。 有时候很容易写出似是而非的代码。比如,在许多应用框架中(application frameworks)都要求Derived classes内的virtual函数代码第一行都是调用base class对应的virtual函数。例如有个Window base class和SpecialWindow derived class。两者都定义了onResize函数,specialWindow中的onResize函数要先调用Window中的onResize函数。下面是一种实现方式,看起来对,但其实是错的。

class Window{public:virtual void onResize(){……};……};class specialWindow:public Window{public:virtual void onResize(){static_cast<Window>(*this).onResize();//将this转为Window,然后调用。这样其实不行。……}};闽南的花市,一开始是来自漳州百花村,

《Effective C++》资源管理:条款26

相关文章:

你感兴趣的文章:

标签云: