C++代码优化(3) |
|
www.nanhushi.com 佚名 不详 |
结构体成员的布局
很多编译器有“使结构体字,双字或四字对齐”的选项。但是,还是需要改善结构体成员的对齐,有些编译器可能分配给结构体成员空间的顺序与他们声明的不同。但是,有些编译器并不提供这些功能,或者效果不好。所以,要在付出最少代价的情况下实现最好的结构体和结构体成员对齐,建议采取这些方法:
按类型长度排序
把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。
把结构体填充成最长类型长度的整倍数
把结构体填充成最长类型长度的整倍数。照这样,如果结构体的第一个成员对齐了,所有整个结构体自然也就对齐了。下面的例子演示了如何对结构体成员进行重新排序:
不好的代码,普通顺序 推荐的代码,新的顺序并手动填充了几个字节
struct { char a[5]; long k; double x; } baz; struct { double x; long k; char a[5]; char pad[7]; } baz;
这个规则同样适用于类的成员的布局。
按数据类型的长度排序本地变量
当编译器分配给本地变量空间时,它们的顺序和它们在源代码中声明的顺序一样,和上一条规则一样,应该把长的变量放在短的变量前面。如果第一个变量对齐了,其它变量就会连续的存放,而且不用填充字节自然就会对齐。有些编译器在分配变量时不会自动改变变量顺序,有些编译器不能产生4字节对齐的栈,所以4字节可能不对齐。下面这个例子演示了本地变量声明的重新排序:
不好的代码,普通顺序 推荐的代码,改进的顺序
short ga, gu, gi; long foo, bar; double x, y, z[3]; char a, b; float baz; double z[3]; double x, y; long foo, bar; float baz; short ga, gu, gi;
避免不必要的整数除法
整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码 推荐的代码
int i, j, k, m; m = i / j / k; int i, j, k, m; m = i / (j * k);
把频繁使用的指针型参数拷贝到本地变量
避免在函数中频繁使用指针型参数指向的值。因为编译器不知道指针之间是否存在冲突,所以指针型参数往往不能被编译器优化。这样是数据不能被存放在寄存器中,而且明显地占用了内存带宽。注意,很多编译器有“假设不冲突”优化开关(在VC里必须手动添加编译器命令行/Oa或/Ow),这允许编译器假设两个不同的指针总是有不同的内容,这样就不用把指针型参数保存到本地变量。否则,请在函数一开始把指针指向的数据保存到本地变量。如果需要的话,在函数结束前拷贝回去。 不好的代码 推荐的代码
?/ 假设 q != r void isqrt(unsigned long a, unsigned long* q, unsigned long* r) { *q = a; if (a >; 0) { while (*q >; (*r = a / *q)) { *q = (*q + *r) >;>; 1; } } *r = a - *q * *q; } // 假设 q != r void isqrt(unsigned long a, unsigned long* q, unsigned long* r) { unsigned long qq, rr; qq = a; if (a >; 0) { while (qq >; (rr = a / qq)) { qq = (qq + rr) >;>; 1; } } rr = a - qq * qq; *q = qq; *r = rr; }
赋值与初始化 先看看以下代码:
class CInt { int m_i;
public: CInt(int a = 0):m_i(a) { cout <;<; ";CInt"; <;<; endl; } ~CInt() { cout <;<; ";~CInt"; <;<; endl; }
CInt operator + (const CInt&; a) { return CInt(m_i + a.GetInt()); }
void SetInt(const int i) { m_i = i; } int GetInt() const { return m_i; } }; 不好的代码 推荐的代码 void main() { CInt a, b, c; a.SetInt(1); b.SetInt(2); c = a + b; } void main() { CInt a(1), b(2); CInt c(a + b); }
这两段代码所作的事都一样,但那一个更好呢?看看输出结果就会发现,不好的代码输出了四个";CInt";和四个";~CInt";,而推荐的代码只输出三个。也就是说,第二个例子比第一个例子少生成一次临时对象。Why? 请注意,第一个中的c用的是先声明再赋值的方法,第二个用的是初始化的方法,它们有本质的区别。第一个例子的";c = a + b";先生成一个临时对象用来保存a + b的值,再把该临时对象用位拷贝的方法给c赋值,然后临时对象被销毁。这个临时对象就是那个多出来的对象。第二个例子直接用拷贝构造函数的方法对c初始化,不产生临时对象。所以,尽量在需要使用一个对象时才声明,并用初始化的方法赋初值。
尽量使用成员初始化列表
在初始化类的成员时,尽量使用成员初始化列表而不是传统的赋值方式。
不好的代码 推荐的代码
籧lass CMyClass { string strName;
public: CMyClass(const string&; str); };
CMyClass::CMyClass(const string&; str) { strName = str; } class CMyClass { string strName; int i;
public: CMyClass(const string&; str); };
CMyClass::CMyClass(const string&;str) : strName(str) { }
不好的例子用的是赋值的方式。这样,strName会先被建立(调用了string的默认构造函数),再由参数str赋值。而推荐的例子用的是成员初始化列表,strName直接构造为str,少调用一次默认构造函数,还少了一些安全隐患。
|
|
|
文章录入:杜斌 责任编辑:杜斌 |
|
上一篇文章: C++代码优化(2) 下一篇文章: c++技巧之二(MFC) |
【字体:小 大】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口】 |
|
|