任何时候都适用的20个C++技巧 <1-4> (The Top 20 C++ Tips of All Time <1-4>)
2010-11-30 14:25 凌云健笔 阅读(3008) 评论(10) 编辑 收藏 举报这些小技巧之所以特别,是因为这些信息通常吧不能在C++书籍或者网站上找到。比如说,成员指针,即使对于高级程序员也是比较棘手,和易于产生bugs的,是应该尽量避免的问题之一。
<翻 by凌云健笔>
What makes these tips special is that the information they provide usually cannot be found in C++ books or Web sites. For example, pointers to members are one of the most evasive, tricky, and bug-prone issues for even advanced users.
by Danny Kalev
====================================
Page 1: Introduction 介绍
接下来的这几条技巧主要集中于实用技术和一些晦涩知识上;它们与特殊的平台、编程领域、或编译器无关。因此,它们适用于所有的C++程序员。本人把这些技巧划分为五大类:编码风格、内存管理、性能提升、面向对象的设计,和标准模板库(STL)五方面的一般准则。
The following tips are a collection of general hands-on techniques and recondite pieces of knowledge not associated with a specific platform, programming domain, or compiler. As such, they can be of use to all C++ programmers. I grouped the tips into five major categories: general guidelines for coding style, memory management, performance enhancement, object-oriented design, and the Standard Template Library (STL).
====================================
First Four: Guidelines for Better Coding Style 较好编程风格所要遵循的一些准则
在这个类别中,所涉及的技巧是各级C++的程序员均会经常提及的问题。举个例子,我很惊讶的发现,有很多具有一定经验的程序员仍旧不知道.h是一种过时的标准头文件标识方式,不会正确的应用名空间,不了解在向临时对象绑定引用时所要遵循的准则。这些问题以及一些其他问题将在这里进行讨论。首先,我们先解释过时的头文件命名符号<xxx.h>与现代的符合标准的<xxx>头文件命名符号之间的区别。接下来,我们探究一些由于编译器限制以及相关的语言规则深奥性质所带来的C++“阴暗角落”;这点往往有许多程序员混淆不清。例如,用逗号分隔的表达式,对右值绑定引用的规则等。最后,我们将学习如何在程序的启动之前调用某个函数。
技巧1:用<iostream.h>还是<iostream>?这不是一个问题!
很多的C++程序员依旧使用<iostream.h>,而非最新的、标准编译生成的<iostream>库。这两个库之间有什么区别呢?首先,针对用.h作为标准头文件的标识符这一问题,五年前就已经不被推荐使用了。在新的代码中再使用这种不被认同的表示方式绝不是一个好主意。在功能方面,<iostream>包括模板化的IO类,它同时支持窄字符和宽字符;而<iostream.h>却只支持以char为导向的流。第三,在C++的iostream接口标准规格在许多微妙的方面发生了变化。所以,<iostream>的接口与实现与<iostream.h>存在着一定得差异。最后,,<iostream>组件声明于std命名空间中,而<iostream.h>组件是全局性的。
因为二者之间存在着这些重大分歧,你不能在同一程序中混合使用两个库。作为一条准则:使用<iostream>代替<iostream.h>,除非你处理了那些只与<iostream.h>兼容的遗留代码。
Tip 1: <iostream.h> or <iostream>?
Many C++ programmers still use <iostream.h> instead of the newer, standard compliant <iostream> library. What are the differences between the two? First, the .h notation of standard header files was deprecated more than five years ago. Using deprecated features in new code is never a good idea. In terms of functionality, <iostream> contains a set of templatized I/O classes which support both narrow and wide characters, as opposed to <iostream.h> which only supports char-oriented streams. Third, the C++ standard specification of iostream's interface was changed in many subtle aspects. Consequently, the interfaces and implementation of <iostream> differ from those of <iostream.h>. Finally, <iostream> components are declared in namespace std whereas <iostream.h> components are global.
Because of these substantial differences, you cannot mix the two libraries in one program. As a rule, use <iostream> unless you're dealing with legacy code that is only compatible with <iostream.h>.
技巧2:左值的引用,要注意!
左值和右值是C++编程的基本概念。从本质上说,右值是一种不能出现在赋值表达式左侧的表达式。相较而言,左值是一个你可以写入数值的对象或者内存块。引用可以指向左值,也可以是右值。但是,由于对右值的语言限制,所以你必须了解在向右值绑定引用时所要遵循的规则。
只要引用绑定的对象是一个const类型,那么就可以对右值绑定引用。这一规则背后的理由很简单:你不能试图去改变一个右值,常量的引用保证了程序不会通过引用去改变右值。在下面的例子中,函数f()输入一个const int的引用:
void f(const int & i);
int main()
{
f(2); /* OK */
}
程序将右值2作为一个参数传入函数f()。在实时运行中,C++会生成一个类型为int值为2的临时对象,并将其与引用i绑定。临时对象和他的引用存在与函数f()从触发到返回整个过程;函数返回后,他们被立即销毁。注意,如果我们声明引用i时没有使用const标识符,函数f()就可以修改它的参数,从而引起未定义的行为。所以,你只能向常量对象绑定引用。
同样的准则适用于自定义对象类性。只有当临时对象是常量时,你才能绑定引用。
Tip 2: Binding a Reference to an Rvalue
Rvalues and lvalues are a fundamental concept of C++ programming. In essence, an rvalue is an expression that cannot appear on the left-hand side of an assignment expression. By contrast, an lvalue refers to an object (in its wider sense), or a chunk of memory, to which you can write a value. References can be bound to both rvalues and lvalues. However, due to the language's restrictions regarding rvalues, you have to be aware of the restrictions on binding references to rvalues, too.
Binding a reference to an rvalue is allowed as long as the reference is bound to a const type. The rationale behind this rule is straightforward: you can't change an rvalue, and only a reference to const ensures that the program doesn't modify an rvalue through its reference. In the following example, the function f() takes a reference to const int:
void f(const int & i);
int main()
{
f(2); /* OK */
}
The program passes the rvalue 2 as an argument to f(). At runtime, C++ creates a temporary object of type int with the value 2 and binds it to the reference i. The temporary and its reference exist from the moment f() is invoked until it returns; they are destroyed immediately afterwards. Note that had we declared the reference i without the const qualifier, the function f() could have modified its argument, thereby causing undefined behavior. For this reason, you may only bind references to const objects.
技巧3:奇怪的逗号分割表达式
逗号分隔的表达式是从C继承而来的。你很有可能会在使用for-循环和while-循环的时候经常使用这样的表达式。然而,在这方面的语言规则还远不直观。首先,让我们来看看什么是逗号分隔的表达式:这种表达式可能包含一个或多个用逗号分隔的子表达式。例如:
if(++x, --y, cin.good()) /*three expressions 三个表达式*/
IF条件包含由逗号分隔的三个表达式。C++确保每表达式都被执行,产生其副作用。然而,整个表达式的值仅是最右边的表达式的结果。因此,只有cin.good()返回true时,上述条件才为真。再举一个逗号表达式的例子:
int j=10;
int i=0;
while( ++i, --j)
{
/*只要j不为0,在循环执行*/
}
Tip 3: Comma-Separated Expressions
Comma-separated expressions were inherited from C. It's likely that you use such expressions in for- and while-loops rather often. Yet, the language rules in this regard are far from being intuitive. First, let's see what a comma separated expression is. An expression may consist of one or more sub-expressions separated by commas. For example:
if(++x, --y, cin.good()) /*three expressions */
The if condition contains three expressions separated by commas. C++ ensures that each of the expressions is evaluated and its side effects take place. However, the value of an entire comma-separated expression is only the result of the rightmost expression. Therefore, the if condition above evaluates as true only if cin.good() returns true. Here's another example of a comma expression:
int j=10;
int i=0;
while( ++i, --j)
{
/*if (j!=0) loop*/
}
技巧4:如何在程序启动前调用函数?
某些应用程序需要在调用主要程序之前开始启动功能。例如,polling(轮询),billing(***),和logger(日志记录)等函数必须在调用实际的程序之前开始。最简单的实现这一目标的方式是调用一个全局对象的构造函数。因为从概念上说,全局对象是在程序开始之构造的,这个函数会在main()开始之前返回。例如:
class Logger
{
public:
Logger()
{
activate_log();
}
};
Logger log; /*global instance*/
int main()
{
record * prec=read_log();
//.. application code
}
全局对象log在main()开始之前完成构造。在构造过程中,log触发了函数activate_log()。当main()开始后,它就可以从日志文件中读取数据。
Tip 4: Calling a Function Before Program's Startup
Certain applications need to invoke startup functions that run before the main program starts. For example, polling, billing, and logger functions must be invoked before the actual program begins. The easiest way to achieve this is by calling these functions from a constructor of a global object. Because global objects are conceptually constructed before the program's outset, these functions will run before main() starts. For example:
class Logger
{
public:
Logger()
{
activate_log();
}
};
Logger log; /*global instance*/
int main()
{
record * prec=read_log();
//.. application code
}
The global object log is constructed before main() starts. During its construction, log invokes the function activate_log(). Thus, when main() starts, it can read data from the log file.
// 续 任何时候都适用的20个C++技巧 <5-8> 内存管理
作者: 凌云健笔
出处:http://www.cnblogs.com/lijian2010/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任