前言:C++在C语言的基础上,补充了C语言的不足,对C语言不合理的地方进行了优化,为类和对象打基础。
1.C++关键字
C++总共有63个关键字,C语言有32个。
2.命名空间
2.1 命名空间的定义
在C/C++中,变量,函数和类是大量存在的,它们都存在于全局作用域中,可能会导致很多冲突,使用命名空间对这些标识符进行进行本地化,避免命名冲突或名字污染。
#include<stdio.h>
int tmp = 1;
int main()
{
int tmp = 2;
printf("%d\n", tmp);
return 0;
}
像这样,C语言无法解决命名冲突的问题,因此C++提供了namespace来解决。
命名空间的定义:
namespace + 命名空间的名字 + { } , { }中即为命名空间的成员。
一个命名空间就定义了一个新的作用域,所有内容仅局限于该命名空间中
命名空间中可以定义变量/函数/类型
namespace N
{
int a= 10;
int b = 20;
int Add(int x, int y)
{
return x + y;
}
struct Node
{
struct Node* next;
int val;
};
}
命名空间还可以嵌套使用
// test.cpp
namespace T1
{
int a = 10;
int Add(int x, int y)
{
return x + y;
}
namespace T2
{
int b = 20;
int sub(int x, int y)
{
return x - y;
}
}
}
一个工程中可以同时存在多个相同的命名空间,编译器最后会合成在一个命名空间中
test.h
namespace T1
{
int c = 30;
int mul(int x, int y)
{
return x * y;
}
}
两个相同的命名空间最后会被合并成一个 ,例如test.h和题test.cpp中的T1最后会被合并成一个T1。
2.2 命名空间的使用
命名空间的使用有三种方式
1:命名空间名词+作用域限定符
int main()
{
printf("%d\n", N::a);
return 0;
}
2:使用using引入命名空间中的成员
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
3:使用using namespace + 命名空间名称 引入
using namespace N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
printf("%d\n", Add(a, b));
return 0;
}
3.C++输入/输出
C++的输入和输出分别使用 cin 标准输入对象(键盘) 和 cout标准输出对象(控制台)
使用时必须包含头文件<iostream>,以及命名空间std。
<< 是流插入运算符,>>是流提取运算符。
使用cin和cout不用手动控制输入输出格式,会自动识别变量类型。
#include<iostream>
// std是C++标准库的命名空间,C++将标准库的定义都放到了这个命名空间中
using namespace std;
int main()
{
int a;
char b;
double c;
cin >> a >> b >> c;
cout << a << " " << b << " " << c << endl;
// endl代表C语言中的 \n , 表示换行
return 0;
}
4.缺省参数
缺省参数是声明或定义函数时为函数的参数制定一个缺省值,当调用函数时,若没有传递参数,默认使用该形参的确省值,否则使用实参。
#include <iostream>
using namespace std;
int Add(int a = 10, int b = 20)
{
return a + b;
}
int main()
{
// 未传参时,默认使用缺省值
cout << Add() << endl;
// 当调用函数传递参数后,默认使用实参
cout << Add(10, 10) << endl;
return 0;
}
缺省参数分类
全缺省
void func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl; // 10
cout << "b = " << b << endl; // 20
cout << "c = " << c << endl; // 30
}
半缺省
void func2(int a, int b = 20, int c = 30)
{
cout << "a = " << a << endl; // 10
cout << "b = " << b << endl; // 20
cout << "c = " << c << endl; // 30
}
注意:
- 半缺省必须从右往左依次给出,不能跳着给
- 不能在函数定义和声明中同时出现
- 缺省值必须是常量或者全局变量
5.函数重载
C++支持在同一作用域中声明几个功能类似的同名函数,这些函数的形参列表(参数个数 ,类型,类型顺序)不同。
// 参数类型不同
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
// 参数个数不同
void func()
{
cout << "func()" << endl;
}
void func(int a)
{
cout << "func(int a)" << endl;
}
// 参数类型顺序不同
void fuc(int a, char c)
{
cout << "fuc(int a, char c)" << endl;
}
void fuc(char c, int a)
{
cout << "fuc(char c, int a)" << endl;
}
int main()
{
cout << Add(10, 20) << endl;
cout << Add(1.1, 2.2) << endl;
func();
func(10);
fuc(10, 'a');
fuc('a', 10);
return 0;
}
6.引用
6.1 引用的定义
引用是给已经存在了的变量给一个别名,它和这个变量共用同一块空间。
语法:类型& 引用变量名 = 引用实体;
void Test1()
{
int a = 10;
int& ra = a;
printf("%p \n", &a); // 0000007B686FFC30
printf("%p \n", &ra); // 0000007B686FFC30
}
注意:引用类型必须和引用实体类型相同
6.2 引用的特性
- 引用定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦有引用实体,不能在引用其他实体
void Test2()
{
int a = 10;
int b = 20;
// 引用定义时必须初始化
int& ra; // error
// 一个变量可以有多个引用
int& ra = a;
int& rra = a;
// 引用一旦有实体,不能再引用其他实体
int& ra = b; / error
}
6.3 常引用
void Test3()
{
const int a = 10;
//int& ra = a; // 无法从“const int”转换为“int& ”
const int& ra = a;
//int& b = 10;
double d = 12.3;
// int& rd = d; // 无法从“double”转换为“int &”
const int& rd = d;
}
6.4 引用的使用场景
做参数
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
6.5 引用和指针的区别
引用在语法上就是一个别名,没有实际空间,和其引用实体公用一块空间;但在底层实现上,引用其实就是按照指针的方式来实现的。
引用和指针的不同点:
引用在创建时必须初始化,而指针不用。
引用一旦初始化引用一个实体以后就不能再引用其他实体了,而指针可以任意改变指向。
有空指针,但没有空引用。
sizeof计算大小时,引用计算的是其引用实体所占空间的大小,而指针的大小始终是4个字节。
有多级指针,但没有多级引用。
访问实体方式不同,指针需要显示解引用,引用编译器自己处理。
7.内联函数
7.1 概念
以inline关键字修饰的函数叫做内联函数,编译器会在函数调用的地方自动展开,内联函数没有函数调用建立栈帧的开销,提高了程序的运行效率。
7.2 特性
1.inline是一种以空间换时间的做法,如果将某个函数定为内联函数,编译器会在编译阶段将函数调用替换为函数体。缺点:内存消耗大 优点:减少函数调用时建立栈帧的开销,提高程序的运行效率。
2.inline方法只是对编译器的一种建议,到底展不展开取决于编译器,一般建议:将函数体较小,不是递归,频繁调用的函数采用inline修饰。
3.inline不建议定义和声明分离,否则导致链接错误,因为inline一旦被展开就没有函数地址了,在链接时就会找不到。
8.auto关键字(C++11)
8.1 auto的定义
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量
在C++11中,标准委员会给了auto新的定义:auto不再是一个储存类型指示符,而是一个全新的类型指示符,编译器在编译阶段会自动推导auto声明的变量。
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'c';
auto d = 15.2;
auto e = TestAuto();
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
return 0;
}
需要注意的是:auto变量在定义时必须初始化,auto并不是变量类型的声明,而是作为“占位符”使用,编译器在编译阶段会将auto替换为变量的实际类型。
8.2 auto的使用
1.auto和引用和指针结合使用
用auto声明指针类型时,auto和auto*没什么区别,但在声明类型引用时必须加上&。
int main()
{
int a = 10;
auto pa1 = &a;
auto* pa2 = &a;
auto& b = a;
cout << typeid(pa1).name() << endl;
cout << typeid(pa2).name() << endl;
cout << typeid(b).name() << endl;
return 0;
}
2.在同一行定义多个变量
auto关键字在同一行定义多个变量时必须是同一类型的变量,否则编译器会报错,实际上编译器只对第一个变量进行了推导,然后用推导出的第一个变量的类型定义后面的变量。
int main()
{
auto a = 10, b = 30;
auto ch1 = 'a', ch2 = 'b';
cout << a << ' ' << b << endl;
cout << ch1 << ' ' << ch2 << endl;
return 0;
}
注意:auto不能做函数的参数,不能用来声明数组。
9. 基于范围的for循环(C++11)
在C++98中,想要遍历一个数组,可采用以下方法:
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < 5; i++)
array[i] *= array[i];
for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); p++)
cout << *p << endl;
}
但 对于一个有范围的集合,由程序员自己说明范围是多余的,有时还会犯错,因此C++11给出了一种新的for循环:基于范围的for循环。for循环括号里面由“ :”分为两部分,第一部分是范围内用于迭代的变量,第二部分表示要迭代的范围。
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto& i : array)
i *= i;
for (auto i : array)
cout << i << endl;
}