[转]你可能不知道的C++(一)

此为《你可能不知道的C++》的第一部分,讨论C&c++,编译单元以及对象

C++&c##

C++ 是 C 的超集,但是 C++ 中的子集 C 跟原始的 C 还是有点不一样。

结构 & 联合###

  • C 的结构(struct)不是一种类型,使用时得带着关键字struct,一般用typedef来避免这种不便。
  • C++ 的结构几乎等价于类,只是缺省的访问权限为public而非private。
  • C++ 的联合(union)可以有成员函数,甚至可以有构造和析构函数。

不带参数的函数###

对 C 来说,一个不带参数的函数意味着可以接受任意参数。所以void f()就相当于void f(...),而下面三个函数指针类型中:

typedef void (foox)();
typedef void (
foo1)(int);
typedef void (*foo2)(void);

foo1和foo2可以隐式地转型为foox,就好比可以从int或char隐式地转型成void*。

要想让一个 C 函数真正没有参数,得用void:

void foo(void);

对 C++ 来说,一个不带参数的函数就是指不接受参数。往参数列表里放void是多余的。

提升void*###

C 会自动提升(promote)void

int pi = malloc(sizeof(int));

函数malloc返回void,赋值给int时不需要显式转型。而 C++ 必须显式转型:

int* pi = static_cast
(int*)(malloc(sizeof(int)));

CONSTS###

C++ 允许 consts 用在常量表达式中:

const int MAX = 4;
int a[MAX + 1];
switch (i) {
case MAX:
...
}

而 C 则必须使用宏:

#define MAX 4

引一段《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.

C 的 consts(特指用 const 关键字修饰的常量)不可以用在常量表达式中。这让 C 的 consts 远不如 C++ 的有用,也让 C 依赖于预处理器,而 C++ 程序员则可以使用有适当类型和作用域的 consts。

前置声明###

C 代码块中,所有声明必须出现在任何程序语句之前,比如函数定义时,先声明所有局部变量:


void foo() {
int ival, p;
/
… */
}

而 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?

size_t

标准库里到处都是size_t的身影:


void *malloc(size_t n);
void *memcpy(void *s1, void const *s2, size_t n);
size_t strlen(char const *s);

回到前面的问题,不难理解以下几点:

  • size_t是sizeof返回值的类型
  • size_t是一个typedef
  • sizeof不是一个函数,它是一个编译时操作符
  • size_t能够表示任何类型理论上可能的数组的最大大小

其实,size_t一般就是unsigned int的typedef,那为什么不直接用unsigned int?在IP16或IP32平台上(即int和指针大小一致时),确实没有问题,但I16LP32就不行了。此外,直接用unsigned long固然没错,但毕竟得多花了几个字节,稍微有点浪费了。反正只要用size_t,你就可以同时得到正确性和可移植性。

数据对齐

请问mixed_data的大小是多少?是 8 吗?


struct mixed_data {
char data1;
short data2;
int data3;
char data4;
};

在 32 位 x86 平台上编译后的样子:


struct mixed_data {
char data1;
char padding1[1];
short data2;
int data3;
char data4;
char padding2[3];
};

为了数据对齐,编译器塞了一些边角料进去,最终的大小为 12

按存储分类的对象
C/C++ 的对象,按存储类型分为以下几种:

  • 自动的(auto, register)
  • 静态的(static)
    自由存储的(free-store)
    关键字auto有点多余,下面两条声明语句其实等价,b前面的auto加不加一个效果:

    {
    int a;
    auto int b;
    }

    到了 C++11,auto这个关键字就被拿来另作他用了:auto可以让编译器从变量的初始化上自动推断出它的类型:
    auto a = std::max(1.0, 4.0); // 编译器推断出 a 的类型为 double

聚合###

首先,什么叫聚合?

对 C 来说,数组和结构是聚合。

对 C++ 来说,除了数组外,满足以下条件的类(或结构)也是聚合:

  • 没有用户声明的构造函数
  • 没有private或protected非静态数据成员
  • 没有基类
  • 没有虚函数

所以,下面几个类型都是聚合:


int[5];

struct person {
std::string name;
int age;
};

boost::array;

第一部分完。
原文地址

你可能感兴趣的:([转]你可能不知道的C++(一))