目录
【前言】
C++是在C的基础上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。
1、C++关键字(C++98)
C++总计63个关键字,C语言32个关键字
2、命名空间
- C/C++中,变量、函数和类都是大量存在的,这些变量名、函数名和类名存在于全局作用域中,可能会导致很多冲突。
- 举个例子:小组成员分工写一个项目,不同程序员可能命名了相同名称的变量或者函数,而这些又是作用于全局的,当程序放到一起去运行时,就会产生重定义冲突。
- 使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。
2.1 namespace定义命名空间
namespace 命名空间名
{
// 命名空间成员
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int a,int b)
{
return a + b;
}
struct Node
{
int val;
struct Node* next;
};
}注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
namespace smile { int a = 0; int b = 1; int Add(int a, int b) { return a + b; } struct Node { int val; struct Node* next; }; }
- 命名空间可以嵌套定义(即一个命名空间里可以再定义其他的命名空间)。
- 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成在同一个命名空间中(ps:一个工程中的test.h和test.cpp中的N1会被合并成一个)。
2.2 命名空间的使用
命名空间使用的三种方式:
- 加命名空间名称及作用域限定符
int main() { printf("%d\n", smile::a); return 0; }
- 使用using将命名空间中某个成员引入
using smile::b; int main() { printf("%d\n", b); return 0; }
- 使用using namespace 命名空间名称 引入
using namespace smile; int main() { printf("%d\n", smile::a); printf("%d\n", b); return 0; }
std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中。
3、C++输入和输出
- cout标准输出对象(控制台)和cin标准输入对象(键盘)是全局的流对象,使用时,必须包含<iostream>头文件以及按命名空间方法使用std。
- <<是流插入运算符,>>是流提取运算符。
4、缺省参数
缺省参数:是声明或定义函数时,为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则使用该形参的缺省值,否则使用指定的实参。
4.1 缺省参数分类
- 全缺省参数:所有形参都给了缺省值。
void Func(int a = 1, int b = 2, int c = 3) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; }
- 半缺省参数:给出部分缺省值
注意:
1、半缺省参数必须从右往左依次给出,不能间隔着给(即给了缺省值的必须在无缺省值的右边,否则会出现以下问题)。
//错误案例 void Func(int a = 10, int b = 20, int c)//有缺省值的出现在无缺省值的左边 { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; } int main { Func(20);//这样调用的时候,20应该传给有有缺省值的a,还是无缺省值的c呢? //按照从左向右的传参顺序应该传给a,但c又没有默认缺省值,这样就会导致c没有值,编译器报错 return 0; }
2、缺省参数不能在函数声明和定义时同时出现。
因为当两个位置给的缺省值不一样时,编译器无法确定该用哪个缺省值。
3、缺省值必须是常量或全局变量。
4、C语言不支持缺省参数(编译器不支持)。
5、函数重载
函数重载:
1、解决的是同一个函数名有着不同的功能的问题。
2、C++允许在同一作用域中声明几个功能类似的同名函数。
3、构成函数重载的条件:形参列表(形参个数 或 类型 或 类型顺序)不同。
注意:函数返回值类型不同不构成重载。
//1、形参个数不同 void f() { cout << "f()" << endl; } void f(int a) { cout << "f(int a)" << endl; } //2、形参类型不同 int Add(int left, int right) { return left + right; } double Add(double left, double right) { return left + right; } //3、形参类型顺序不同 void f(int a, char b) { cout << "f(int a, char b)" << endl; } void f(char b, int a) { cout << "f(char b,int a)" << endl; }
5.1 C++支持函数重载的原理——名字修饰
- 程序运行要经历几个阶段:预处理、编译、汇编、链接。
- 多个文件,隔离编译,一起链接,编译后函数名可能会改变,链接会去寻找所调用的函数。
- C编译器:没有对形参列表不同的同名函数进行修饰,所以同名函数链接时,链接器无法区分链接哪个函数。
- C++编译器:编译器将函数形参列表信息添加到修饰后的名字中,这样链接器就可以区别同名但不同形参列表的函数了。
- c++通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
6、引用
6.1 引用概念
1、给已存在的变量取个别名,它和它引用的变量共用同一块内存空间。
2、形式:类型& 引用变量名 = 引用实体。
3、引用类型必须和引用实体同类型。
6.2 引用特性:
1、引用在定义时必须初始化
2、一个变量可以有多个引用
3、引用一旦引用一个实体,便不能再引用其他实体。
6.3 常引用
1、引用过程中,权限不能放大
const int a = 0;
int& b = a; //错误
2、但权限可以平移或缩小
int x = 0;
int& y = x; //一个变量可以有多个引用
const int& z = x;
++x; //可以
++z; //不可以
3、类型转换时会产生临时变量,临时变量具有常性(相当于const修饰)
double dd = 1.11;
int ii = dd;
int & rii = dd; //不可以,dd拷贝给int临时变量,临时变量再取别名为rii,这个过程权限放大了,从const int型放大成了int型的。
const int& rii = dd; //可以,权限的平移。
6.4 使用场景
1、做参数
//引用做参数 void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
意义:对于大对象、深拷贝类对象,可以减少拷贝,提高效率。
2、做返回值
//引用做返回值 int& Count() { static int n = 0; n++; //... return n; }
- 传值返回:要生成临时变量,传的值拷贝给临时变量,临时变量在做返回值给接收变量
- 传引用返回:对于大对象、深拷贝类对象,传引用返回,不会再生成临时变量,这样可以减少拷贝,提高效率。
总结:
1、基本任何场景都可以用引用传参。
2、谨慎用引用做返回值:函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果不在了(还给系统了),则必须使用传值返回。
6.5 引用和指针的区别
- 语法层面:引用不开空间,是取别名。
- 底层汇编指令实现的角度:引用是类似指针的方式实现的。
引用和指针的不同点:
1、引用是给变量取别名,指针是取变量地址。
2、引用在定义时必须初始化,指针没有要求。
3、引用一旦确定引用实体,便不能再引用其他实体。而指针可以在任何时候指向任何一个同类型实体。
4、没有NULL引用,但有NULL指针。
5、在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。
6、引用自加即引用的实体值加1,指针自加即指针向后偏移一个类型的大小。
7、有多级指针,但没有多级引用。
8、访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
9、引用比指针使用起来相对更安全。
7、内联函数
- 内联函数:以inline修饰的函数,编译时C++编译器会在调用内联函数的地方展开函数定义代码
- 好处:没有函数调用建立栈帧的开销,提升程序运行的效率。
- 适用:短小的频繁调用的函数。inline对于编译器仅仅只是一个建议,最终是否成为inline,编译器自己决定。
- 建议:inline函数的声明和定义不要分开。
8、auto关键词(C++11)
auto:自动推导类型,根据右边自动推导左边。
注意:
- 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需根据初始化表达式来推导auto的实际类型。
- auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译时会将auto替换为变量实际的类型。
8.1 auto的使用细则
1、auto与指针/引用结合使用:用auto声明指针类型时,用auto和auto*没有任何区别,但是auto声明引用类型时则必须加&。
int x =10;
举例:
- auto a = &x; //可以,因为右边x加了取地址符,编译器较容易推出左边a的类型。
- auto* b = &x;//可以
- auto c = x;//这样就不是引用类型取别名,而是int类型定义c
- auto& c = x;//正确的引用
2、在同一行定义多个变量,auto必须始终推导为同一类型。
8.2 auto不能推导的场景
1、auto不能作为函数形参类型
- void TestAuto(auto a) { } //错误
2、auto不能直接用来声明数组
- void TestAuto(auto a)
- {
- int a[ ] = {1, 2, 3};
- auto b[ ] = {4, 5, 6 };
- }
9、基于范围的for循环(C++11)
- 说明:对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时还容易犯错。因此C++11中引入了基于范围的for循环。
- 形式:for循环后的括号内由冒号:分为两部分,第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
9.1 范围for的使用条件
1、for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围。
2、迭代的对象要实现++和==的操作。
10、nullptr空指针(C++11)
- NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++作为新关键字引入的。
- 在C++11中,sizeof(nullptr)与sizeof((void*)0)所占的字节数相同。
- 为了提高代码的健壮性,在后续表示指针空值最好使用nullptr。