常见错误7:无视基础语言的精妙之处
大多数C++软件工程师都自信满满地认为iziji对所谓C++的“基础语言”,也就是C++继承
自C语言那部分了如指掌。 实际情况是,即使经验丰富的C++软件工程师有时也会对最基础
的C/C++语句和运算符的某些妙用一无所知。
很多软件工程师都把“如果条件运算符表达式的两个选择结果都是左值,那么这个表达式本身
就是个左值”这回事抛在脑后了。
C++代码的很多上下文(比如构造函数的成员初始化列表,或抛出异常时的throw表达式,等等),
除了表达式别无选择。
此内建的索引运算符只是对于某些指针算术和反引用运算符的一种简写法。
case语句的标签必须是整形常量性的表达式。换句话说,编译器必须能在编译时就算出这些
表达式的值来。
大多数C++软件工程师都自信满满地认为iziji对所谓C++的“基础语言”,也就是C++继承
自C语言那部分了如指掌。 实际情况是,即使经验丰富的C++软件工程师有时也会对最基础
的C/C++语句和运算符的某些妙用一无所知。
很多软件工程师都把“如果条件运算符表达式的两个选择结果都是左值,那么这个表达式本身
就是个左值”这回事抛在脑后了。
C++代码的很多上下文(比如构造函数的成员初始化列表,或抛出异常时的throw表达式,等等),
除了表达式别无选择。
此内建的索引运算符只是对于某些指针算术和反引用运算符的一种简写法。
case语句的标签必须是整形常量性的表达式。换句话说,编译器必须能在编译时就算出这些
表达式的值来。
switch语句的平凡语法暗示着我们能够把语句块写成比上面的例子更结构化的形式。
bool.cpp
#include <iostream>
typedef unsigned short Bits;
inline Bits repeated( Bits b, Bits m )
{ return b & m & (b & m)-1; }
void dowop1( Bits options ) {
int ctr = 0;
for( int i = 0; i < 8; ++i )
if( options & 1<<(8+i) )
if( ctr++ ) {
std::cerr << "Too many options selected" << std::endl;
break;
}
}
void dowop2( Bits options ) {
if( repeated( options, 0XFF00 ) )
std::cerr << "Too many options selected" << std::endl;
}
void boolTest() {
std::cout << "=== bool test ===" << std::endl;
Bits opts[] = { 0x0000, 0x1000, 0x1011, 0x2100, 0xf000 };
for( int i = 0; i < sizeof(opts)/sizeof(opts[0]); ++i ) {
std::cerr << "Options: " << std::hex << opts[i] << std::endl;
dowop1( opts[i] );
dowop2( opts[i] );
}
}
iter.cpp
#include <iostream>
class Node;
class Preorder;
class Postorder;
class NaryTree {
public:
int count()
{ return size; }
public:
Node *root;
int size;
};
typedef short Index;
class Node {
public:
Index parent;
Index sibling;
Index lchild;
// other members...
int value;
friend class Preorder;
friend class Postorder;
};
class Preorder {
public:
Preorder( NaryTree &t )
: cur( t.root ), end( t.root+t.count()-1 ) {}
bool next();
Node *get()
{ return cur; }
private:
Node *cur, *end;
};
inline bool
Preorder::next() {
return cur == end
? 0
: (++cur, 1);
}
class Postorder {
public:
Postorder( NaryTree &t )
: cur( t.root ), pc( START )
{ next(); }
bool next();
Node *get()
{ return cur; }
private:
Node *cur;
enum {
START,
LEAF,
INNER,
DONE
} pc;
bool parent()
{ return cur->parent ? (cur+=cur->parent, true) : false; }
bool sibling()
{ return cur->sibling ? (cur+=cur->sibling, true) : false; }
bool lchild()
{ return cur->lchild ? (cur+=cur->lchild, true) : false; }
};
//对于基础语言的边角部分的理解,有时候相当有用。switch语句的性质,在C++编译器中
//做出了一个复杂数据结构内部迭代的有效实现
bool
Postorder::next() {
switch( pc )
case START:
while( true )
if( !lchild() ) {
pc = LEAF;
return true;
case LEAF:
while( true )
if( sibling() )
break;
else
if( parent() ) {
pc = INNER;
return true;
case INNER: ;
}
else {
pc = DONE;
case DONE: return false;
}
}
return true; // will never be executed
}
void iterTest() {
using namespace std;
cout << "=== iter test ===" << endl;
Node tree[5] = {
{ 0,0,1, 101 },
{-1,3,1, 102 },
{-1,1,0, 103 },
{-2,0,0, 104 },
{-4,0,0, 105 }
};
NaryTree n;
n.root = tree;
n.size = 5;
cout << "Preorder: ";
Preorder pre(n);
do {
cout << pre.get()->value << ' ';
} while( pre.next() );
cout << endl;
cout << "Postorder: ";
Postorder post(n);
do {
cout << post.get()->value << ' ';
} while( post.next() );
cout << endl;
}
main.cpp
// Note: quick hack to run tests. Not a good coding practice!
extern void iterTest();
extern void boolTest();
#include<iostream>
int main() {
boolTest();
iterTest();
getchar();
return 0;
}
输出
=== bool test ===
Options: 0
Options: 1000
Options: 1011
Options: 2100
Too many options selected
Too many options selected
Options: f000
Too many options selected
Too many options selected
=== iter test ===
Preorder: 101 102 103 104 105
Postorder: 103 104 102 105 101