此为《你可能不知道的C++》的第一部分,讨论C&c++,编译单元以及对象
C++&c##
C++ 是 C 的超集,但是 C++ 中的子集 C 跟原始的 C 还是有点不一样。
结构 & 联合###
- C 的结构(struct)不是一种类型,使用时得带着关键字struct,一般用typedef来避免这种不便。
- C++ 的结构几乎等价于类,只是缺省的访问权限为public而非private。
- C++ 的联合(union)可以有成员函数,甚至可以有构造和析构函数。
不带参数的函数###
对 C 来说,一个不带参数的函数意味着可以接受任意参数。所以void f()就相当于void f(...),而下面三个函数指针类型中:
<pre>typedef void (foox)();
typedef void (foo1)(int);
typedef void (*foo2)(void);
</pre>
foo1和foo2可以隐式地转型为foox,就好比可以从int或char隐式地转型成void*。
要想让一个 C 函数真正没有参数,得用void:
<pre>void foo(void);</pre>
对 C++ 来说,一个不带参数的函数就是指不接受参数。往参数列表里放void是多余的。
提升void*###
C 会自动提升(promote)void:
<pre>int pi = malloc(sizeof(int));</pre>
函数malloc返回void,赋值给int时不需要显式转型。而 C++ 必须显式转型:
<pre>int* pi = static_cast
(int*)(malloc(sizeof(int)));</pre>
CONSTS###
C++ 允许 consts 用在常量表达式中:
<pre>const int MAX = 4;
int a[MAX + 1];
switch (i) {
case MAX:
...
}</pre>
而 C 则必须使用宏:
<pre>#define MAX 4</pre>
引一段《C++ 的设计和演化》的原文:
(Bjarne Stroustrup, The Design and Evolution Of C++, 3.8)
In C, consts may not be used in constant expressions. This makes consts far less useful in C than in C++ and leaves C dependent on the preprocessror while C++ programmers can use properly typed and scoped consts.
</br>
C 的 consts(特指用 const 关键字修饰的常量)不可以用在常量表达式中。这让 C 的 consts 远不如 C++ 的有用,也让 C 依赖于预处理器,而 C++ 程序员则可以使用有适当类型和作用域的 consts。
前置声明###
C 代码块中,所有声明必须出现在任何程序语句之前,比如函数定义时,先声明所有局部变量:
<pre>
void foo() {
int ival, p;
/ … */
}
</pre>
而 C++ 的声明,诸如int ival;,其自身就是一个程序语句,也因此可以出现在程序文本中的任何位置。
编译单元##
C/C++ 中的一个源文件(.c, .cpp, .cc)就是一个编译单元(compilation unit)。
头文件(.h, .hpp)不是编译单元,是不能单独编译的。
源文件经过预处理,先搞定下面这些东西:
- 宏:包括用户定义的,和预定义的(__cplusplus, FILE, ...)
- 包含语句:源文件中的include语句全部展开
- 条件编译: #if, #else, #ifudef, ...#error, #warning, ...
预处理过的源文件,经过编译,生成对象文件(.o, .obj)。对象文件经过链接或打包,生成可执行文件或程序库。虽然这里的步骤不太严格,但是大抵就是这样。
如果你对预处理的结果很感兴趣,可以试试编译器的预处理命令:gcc -E (GCC),cl /E or /P (VC)。
对象##
这里所说的对象(object),泛指一切类型的实例,不只是类的实例。
关于对象,我们将探讨以下几个方面:
- 对象的大小(size)
- 按存储(storage)分类的对象
- 聚合(aggregate)
对象的大小###
先来考虑几个问题:
- sizeof是一个函数吗?
- 你知道sizeof(int), sizeof(long)各为多少吗?
- 为什么应该用size_t?
<strong>size_t</strong>
标准库里到处都是size_t的身影:
<pre>
void *malloc(size_t n);
void *memcpy(void *s1, void const *s2, size_t n);
size_t strlen(char const *s);
</pre>
回到前面的问题,不难理解以下几点:
- size_t是sizeof返回值的类型
- size_t是一个typedef
- sizeof不是一个函数,它是一个编译时操作符
- size_t能够表示任何类型理论上可能的数组的最大大小
其实,size_t一般就是unsigned int的typedef,那为什么不直接用unsigned int?在IP16或IP32平台上(即int和指针大小一致时),确实没有问题,但I16LP32就不行了。此外,直接用unsigned long固然没错,但毕竟得多花了几个字节,稍微有点浪费了。反正只要用size_t,你就可以同时得到正确性和可移植性。
<strong>数据对齐</strong>
请问mixed_data的大小是多少?是 8 吗?
<pre>
struct mixed_data {
char data1;
short data2;
int data3;
char data4;
};
</pre>
在 32 位 x86 平台上编译后的样子:
<pre>
struct mixed_data {
char data1;
char padding1[1];
short data2;
int data3;
char data4;
char padding2[3];
};</pre>
为了数据对齐,编译器塞了一些边角料进去,最终的大小为 12
<strong>按存储分类的对象</strong>
C/C++ 的对象,按存储类型分为以下几种:
- 自动的(auto, register)</br>
- 静态的(static)
自由存储的(free-store)
关键字auto有点多余,下面两条声明语句其实等价,b前面的auto加不加一个效果:
<pre>
{
int a;
auto int b;
}
</pre>
到了 C++11,auto这个关键字就被拿来另作他用了:auto可以让编译器从变量的初始化上自动推断出它的类型:
<pre>auto a = std::max(1.0, 4.0); // 编译器推断出 a 的类型为 double</pre>
聚合###
首先,什么叫聚合?
对 C 来说,数组和结构是聚合。
对 C++ 来说,除了数组外,满足以下条件的类(或结构)也是聚合:
- 没有用户声明的构造函数
- 没有private或protected非静态数据成员
- 没有基类
- 没有虚函数
所以,下面几个类型都是聚合:
<pre>
int[5];
struct person {
std::string name;
int age;
};
boost::array;
</pre>
<strong>第一部分完。</strong>
原文地址
网友评论