阅读本文之前,可先参考c 语言核心
c++相对于 c 语言,主要是增加了面向对象和模板编程
c++中的实体
C++ 程序中的实体包括:值、对象、引用、 结构化绑定 (C++17 起)、函数、枚举项、类型、类成员、模板、模板特化、命名空间和形参包。预处理器宏不是 C++ 实体。
对象类型:非函数类型、非引用类型且非 void 类型的
所以,以下实体都不是对象:值,引用,函数,枚举项,类型,类的非静态成员,位域,模板,类或函数模板的特化,命名空间,形参包,和 this。
名字查找和命名空间
作用域:文件作用域(全局作用域)、命名空间作用域、类作用域、块作用域、枚举作用域、函数作用域、函数形参作用域、模板形参作用域
内部链接和外部链接
内部链接:其他编译单元无法访问的名称,拥有内部链接,相反是外部链接
以下为内部链接:
- 所有声明
- 类型(struct/union/enum 等)
- 命名空间中的 static 函数和变量以及 const 常量
- inline 函数
以下为外部链接:
- 命名空间中的非 static 函数和变量
- 类成员函数(包含成员函数和 static 成员函数)和静态成员变量
类型比较
c++有如下类型:
- 基本类型
- void
- nullptr:空指针
- 算术类型
- 整数类型
- bool 类型
- 字符类型
- 窄字符:(char、signed char、unsigned char、char8_t)
- 宽字符:(char16_t、char32_t、wchar_t)
- 整数类型:各种限定的 int
- 浮点类型:(float 、 double 、 long double)
- 整数类型
- 复合类型
- 数组类型
- 函数类型
- 指针类型(包括对象指针,函数指针,成员函数指针,数据成员指针)
- 引用类型
- 枚举类型
- 类类型
- 联合体类型
基本类型异同
空指针字面量
为什么需要空指针字面量?
空指针用于表示一个无效的指针,它的值为 0(早期 C 语言的实现中可能有非 0 空指针,现在已经不用)。对指针置 NULL 即标记指针无效,避免“野指针”的恶果
c 语言空指针定义于<stddef.h>,它可以被定义为 ((void*)0), 0 或 0L,这取决于编译器供应商。
1 | // 兼容 C++ : |
c++中空指针定义如下:
1 |
|
问题一:为什么 c++把 NULL 定义为 0,而不是 void?
因为 c++中不允许(void)到其他类型指针的隐式类型转换
1 |
|
结论:c++中,NULL 只能被定义为 0
问题二:有了 NULL 表示空指针,c++11 为什么增加 nullptr_t ?
当 NULL 被定义为 0 后,在函数重载时,会发生歧义,如下
1 | void Func(char *); |
c++11 的解决方案,定义个 std::nullptr_t 类型,该类型定义了转到任意指针类型的转换操作符,同时不允许该类型的对象转换到非指针类型
结论:用 nullptr 类型表示无效指针后,既不需要初始化时显示转换的麻烦,又避免 NULL 定义为 0 时的函数重载问题,而保留 NULL 只是为了向后兼容。所以 c 中使用 NULL,c++11 中使用 nullptr。
bool 类型
c99 开始,增加关键字_Bool 支持布尔类型
1 |
c99 后,bool,true,false 为宏定义,在<stdbool.h>中
c++中,bool,true,false 均为关键字
字符类型
c++中,目前有以下内置字符类型(关键字):
char
char8_t (C++20 起):UTF-8 字符表示的类型,char8_t utf8[] = u8”我”
char16_t (C++11 起):UTF-16 字符表示的类型,char16_t utf16[] = u”我”
char32_t (C++11 起):UTF-32 字符表示的类型,char32_t utf32[] = U”我”
wchar_t: 实现定义,windows 为 16 位 short 类型,gcc 为 32 位 int 类型,定宽字符
c 语言中,使用宏定义实现:
1 | typedef unsigned short wchar_t; // wctype.h |
字符串使用原则:
程序内部使用 char8_t(UTF-8 编码)字符串,既可以表示所有字符,又不浪费空间
求字符串长度等操作时,转换为 wchar_t 宽字符串方便操作,char16_t 和 char32_t 存在字节序问题,不建议使用
注意:宽字符和窄字符相互转换,或者输出到控制台时,需要设定正确的 locale,才能正确解析窄字符串
引用类型
c++增加引用类型,表示一个标识符的别名,故不占用存储空间
没有数组的引用,没有指针的引用,没有引用的引用
面向对象
通过 class 或者 struct 支持面向对象编程
编译器默认会定义的成员函数:
1 | class A { |
c++中的多态:
函数重载:可以定义不同参数的相同名称的函数,编译期,原理是编译器会产生不同的符号
多态:基类中定义的虚函数,可以被子类覆盖,运行时调用不同的函数
多态原理:通过虚函数表和虚表指针实现,编译器为每个定义了虚函数的类分配一个虚函数表,表中存放的是这个类所有虚函数的指针,每个类对象的最前面存放的是虚函数表指针,
模板编程
通过模板编程支持范型编程,让程序员编写与类型无关的通用代码,比如通用算法
1 | // 函数模板 |
模板有显式实例化,隐式实例化,特化(具体化)
隐式实例化:运行期间,根据参数动态生成模板实例
显式实例化:编译期间,生成对应的实例
特化:针对自定义参数,不能生成对应实例时,跟显示实例化一样,编写针对特定类型的模板