C++ 和 C 语言的联系
C++ 名称中的 ++ 其实就是 C 语言中的递增运算符(++),这个名称也表明 C++ 是 C 语言的扩充版本。 ?
那么 C++ 是在 C 语言的基础上添加了什么? C++ 在 C 语言的基础上添加了面向对象编程和泛型编程的支持。面向对象编程的特性带来了全新的编程方法,这种方法可以应付复杂程度不断提高的现代编程任务。C++ 的模板特性则提供了另一种全新的编程方法 —— 泛型编程。这也使得 C++ 融合了 3 种不同的编程方式:C 语言传统的面向过程编程、C++ 新增的面向对象编程(Class)、C++ 模板支持的泛型编程。 C++ 在 C 语言的基础上添加面向对象编程(OOP),这也使得 C++ 既能像 C 语言那样紧密联系硬件,也能使用 OOP 部分将涉及的概念联系起来。
前面我们提到了 C++ 在 C 语言的基础上添加了面向对象编程和泛型编程这两个编程方式,这其实是一个大方向上的变化。C++ 为了支持面向对象编程,新增了异常、运行阶段类型识别等知识;为了支持泛型编程,新增了模板和标准模板库(STL)等知识。 ISO C++ 标准还吸收了 ANSI C 标准,因为 C++ 应尽量是 C 语言的超集,这意味着在理想情况下,任何有效的 C 程序都应是有效的 C++ 程序。但是,需要知道的是,C++ 和 C 语言依旧有一些细节上的差异。
C++ 和 C 语言的差异
C++ 在 C 语言的基础上新增了面向对象编程和泛型编程两大特性,这使得 C++ 新增了很多特性。 针对面向对象编程,C++ 提供了 class 关键字来创建类的声明、允许用户为自定义的类型进行运算符重载、支持函数重载、提供了异常处理机制。 针对泛型编程,C++ 提供了模板编程,并且还封装了标准模板库 STL。
基本数据类型
1. 内置基本数据类型
C 语言内置的整型数据类型有 char、short、int、long、long long,没有布尔类型,如果想要声明一个布尔类型的变量,需要 #inclued <stdbool.h> 。导入 stdbool.h 这个头文件之后才可以用 _Bool 来声明一个布尔类型变量,以及使用 true 和 false 表示真假。 而 C++ 将 bool 类型作为内置的基本数据类型,用 true 和 false 表示真假。
2. 变量初始化
除了 C 语言支持的初始化方法,C++ 还有 C 语言没有的初始化语法。
int olws = 1001;
int nlws1(1001);
int nlws2 = {1001};
int nlws3{1001};
数组等复合类型使用 { } 初始化时也可以省略等号(=),但不能使用 () 来初始化。 ?
复合数据类型
1. 字符串
除了 C-风格字符串之外,C++ 可以使用 string 类来表示字符串。string 存储字符串比字符数组更灵活。 ?
另外 C++11 新增了原始字符串。
cout << R"(hello,"Tim", good)" << endl;
2. 结构
C++ 的结构中允许有函数,和 C++ 的 class 的差别在于 struct 的默认访问权限是 public,而 class 的默认访问权限是 private。 C 语言的结构中不允许有函数,但可以通过声明函数指针的方式来实现。 ?
使用结构描述创建变量时,C++ 可以省略 struct 关键字,而 C 语言不允许省略 struct。
基本语法
1. 循环
C++ 新增了基于范围的 for 循环,这简化了一种常见的循环任务:对数组、容器类的每个元素执行相同的操作。 例如,打印数组的元素:
int arr[10] = {1, 3, 4, 2, 7, 1, 9, 10, 6, 7};
for (int x : arr) {
std::cout << x << std:: endl;
}
如果需要改变元素的值,则需要使用引用:
int arr[10] = {1, 3, 4, 2, 7, 1, 9, 10, 6, 7};
for (int &x : arr) {
x = x << 1;
}
for (int x : arr) {
std::cout << x << " ";
}
std::cout << std:: endl;
2. 逻辑运算符
C++ 中可以使用 and、or、not 来代替逻辑运算符 &&、||、!,在 C++ 中 and/or/not 是保留字,不需要像 C 语言那样包含 <iso646.h> 头文件,C++ 不要求使用头文件。 C++ 中 and、or、not 是保留字,虽然不是关键字,但是也不能用作变量名。
函数
1. 函数原型
- 省略返回值。C 语言的函数头可以省略返回值,此时默认是 int 类型的返回值。而 C++ 逐步淘汰了这种用法。
- 参数列表中的 void。在 C 语言中,参数列表中为空意味着对是否接受参数保持沉默,是否接受参数需要看函数定义,而参数列表中使用 void 表示函数不接受任何参数。而 C++ 中,参数列表中为空和 void 是等效的。
PS:对于这一点也可以看作是 C 语言不支持函数重载。 ?
函数原型可以确保编译器能够正常处理函数返回值、检查使用的参数数目是否正确、使用的参数类型是否正确。 对于参数类型不匹配的情况,C++ 和 C 语言的处理方式不同。例如,如果函数参数是 int 类型(假设是 32 位),而程序员传递了一个 double 类型(假设 64 位)的参数。C 语言会检查 double 的前 32 位,并试图将其解释为一个 int 值。C++ 则是自动将传递的值转换为原型中指定的类型,条件是两者都是算术类型。 但是 C++ 的自动类型转换也不能避免所有可能的错误,仅当有意义时,原型才会类型转换。
2. 函数参数传值
C++ 在 C 语言的值传递和引用传递的基础上新增了引用传递。 ?
引用和指针的区别:
- 引用必须在声明时进行初始化,且一旦绑定对象就不能再和其他对象绑定了,对于这一点可以将引用视为指针常量;
- 引用一定不为空;
- 引用效率比指针更高,因为指针还需要分配内存,而引用不需要。
- 在函数声明使用引用形式的形参,使用函数时需要注意,如果传递的实参是非左值或者与引用参数类型不匹配的值时,实际上是传递了临时变量,这样在函数中修改这个变量不会影响实参的值。 —— 《C++ Primer Plus》262 页,将引用用作函数参数。
3. 重载
C++ 支持函数重载和运算符重载。 ?
函数重载:函数名相同,参数列表不同的函数就是重载。 ?
运算符重载:除成员访问运算符 (.)、成员指针访问运算符 (*)、域运算符(::)、长度运算符 (sizeof)、条件运算符(?:) 这五个运算符之外的其他运算符都可以被重载。前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和 sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征。 例如,可以用 + 来拼接两个字符串。
4. 内联函数
C++ 新增内联函数替代宏函数,后来 C 语言也引入了内联函数。同样的,C++ 使用 const 关键字声明常量来代替宏定义,后来 C 语言也引入了 const 关键字。
5. 函数参数的默认值
C++ 支持函数参数使用默认值,使用默认值时需要注意不要和函数重载冲突。
6. 函数模板
这是 C++ 支持泛型编程而提供的新特性。 ?
内存模型和命名空间
1. 动态分配内存
首先,C++ 中使用 malloc() 等动态分配内存的库函数时,类型转换更加严格。 在 C 语言中允许将 void* 类型的数据赋值给其他类型的指针
int* p = malloc(10 * sizeof(int));
free(p);
在 C++ 中将 void* 类型的数据赋值给其他类型的指针必须使用强制类型转换。
int* p = malloc(10 * sizeof(int));
int* q = (int*) malloc(10 * sizeof(int));
free(q);
?
其次,C++ 可以使用 new 和 delete 运算符来申请和释放内存。
2. auto 关键字
C 语言中 auto 关键字用来表示声明的变量是自动存储类型的变量,而 C++ 中 auto 关键字表示自动类型变量,可以根据初始化的值来判断变量的类型。 另外,C++11 使用 auto 进行后置返回类型声明来解决函数模板的一些问题。
template <typename T1, typename T2>
auto f(T1 & x, T2 & y) -> decltype(x + y);
3. register 关键字
在 C 语言中,register 关键字用来建议编译器使用 CPU 寄存器来存储自动变量,被 register 修饰的变量无法取地址。 在 C++11 之前,register 关键字的用法始终未变,但是在 C++11 中,关键字 regitster 只是显式地指出变量是自动的,这与 auto 以前的用途完全相同。
4. const 关键字
在 C++ 中,const 全局变量的链接性为内部的,也就是说,在 C++ 看来,全局 const 定义就像使用了 static 说明符一样。
const int a = 10;
static const int a = 10;
5. 作用域解析运算符(::)
C++ 比 C 语言更进一步 —— 它提供了作用域解析运算符(::)。 放在变量名前面时,该运算符表示使用变量的全局版本。
8. 头文件命名
C 语言的头文件都是以 h 作为后缀扩展名。例如,math.h、stdio.h、stdlib.h 等。这种方式可以通过文件名称识别文件类型为头文件。 C++ 的头文件没有扩展名,有些 C 语言的头文件被转换为 C++ 头文件,这些文件的扩展名会被去掉,并添加了 c 作为前缀。例如,C++ 版本的 math.h 为 cmath。对于纯粹的 C++ 头文件而言,去掉 h 不只是形式上的变化,没有 h 的头文件可以包含名称空间。
PS:名称空间也是 C++ 的一个新特性。
由于 C 语言使用不同的文件扩展名来表示不同文件类型,因此用一些特殊的扩展名来表示 C++ 头文件是有道理的,C++ 标准制定委员会也这样认为。但问题在于究竟使用哪种扩展名,因此最终他们一致决定同意不使用任何扩展名。
|