文章目录
cpp
关键字(cpp98
)
c++
共有63
个关键字,c
语言共有32
个关键字
c++
关键字:
asm
do
if
return
try
continue
auto
double
inline
short
typedef
for
bool
dynamic_cast
int
signed
typeid
public
break
else
long
sizeof
typename
throw
case
enum
mutable
static
union
wchar_t
catch
explicit
namespace
static_cast
unsigned
default
char
export
new
struct
using
friend
class
extern
operator
switch
virtual
register
const
false
private
template
void
true
const_cast
float
protected
this
volatile
while
delete
goto
reinterpret_cast
命名空间
在
c/c++
中,变量,函数和类都是大量存在的,这些变量、函数和类的名称将存在于全局作用域中,可能会导致命名冲突。使用命名空间可以解决这个冲突。
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或者名字污染。用
namespace
关键字。
定义
// 下面的代码是错误的 // pow 在math.h 中被定义为函数 // 当在预处理的时候 会将头文件展开 两个pow产生的冲突 // 但可以在局部定义这个名字 局部和全局相同时 局部会覆盖全局 // 可以将变量名放在不同的命名空间来解决这个问题 #include <iostream> #include <math.h> int pow = 30; int main() { printf("%d\n", pow); return 0; }
// 命名空间的定义 // 普通的命名空间 // namespace 加 命名空间的名称 方括号内跟着属于这个命名空间的变量 // 可以使用域作用限定符 和 属于这个域的变量来使用和访问这个变量 域名::变量名 // 命名空间的作用范围是全局的 #include <iostream> #include <math.h> namespace ToodlesFate { int pow = 30; } int main() { printf("%d\n", ToodlesFate::pow); return 0; }
// 命名空间的嵌套 #include <iostream> #include <math.h> namespace ToodlesFate1 { int pow = 30; int sqrt = 20; namespace ToodlesFate2 { int Add(int a, int b) { return a + b; } namespace ToodlesFate3 { int sub(int d, int f) { return d - f; } } } } int main() { // 命名空间中变量的使用 printf("%d\n", ToodlesFate1::pow); printf("%d\n", ToodlesFate1::ToodlesFate2::Add(2, 3)); printf("%d\n", ToodlesFate1::ToodlesFate2::ToodlesFate3::sub(5, 1)); return 0; }
// 同一个工程中允许有多个相同的命名空间 编译器最后会合成一个同一个命名空间 // 对于最外层 // error // 说明会将相同命名空间和在一起 namespace ToodlesFate1 { int a = 10; int b = 20; namespace func1 { int add(int x, int y) { return x + y; } } } namespace ToodlesFate1 { int a = 10; int b = 20; namespace func2 { int mul(int x, int y) { return x * y; } } } // 下面是一个正确的 #include <iostream> namespace ToodlesFate1 { int a = 10; int b = 20; namespace func1 { int add(int x, int y) { return x + y; } } } namespace ToodlesFate1 { int c = 10; int d = 20; namespace func2 { int mul(int x, int y) { return x * y; } } } int main() { printf("%d\n", ToodlesFate1::func1::add(2, 3)); printf("%d\n", ToodlesFate1::func2::mul(2, 3)); return 0; }
使用
使用命名空间的内容,有三种方式
- 命名空间名称及域作用限定符
- 使用
using
将命名空间中的成员导入- 使用
using namespace
将命名空间导入// 命名空间名称及域作用限定符 #include <cstdio> namespace ToodlesFate { int a = 10; int b = 20; int Add(int x, int y) { return x + y; } namespace ToodlesFate1 { int c = 30; int d = 40; int sub(int x, int y) { return x - y; } } } int main() { printf("%d\n", ToodlesFate::a); printf("%d\n", ToodlesFate::b); printf("%d\n", ToodlesFate::Add(2, 3)); printf("%d\n", ToodlesFate::ToodlesFate1::c); printf("%d\n", ToodlesFate::ToodlesFate1::d); printf("%d\n", ToodlesFate::ToodlesFate1::sub(5, 2)); return 0; }
// 使用using将命名空间中的成员导入 #include <cstdio> namespace ToodlesFate { int a = 10; int b = 20; int Add(int x, int y) { return x + y; } namespace ToodlesFate1 { int c = 30; int d = 40; int sub(int x, int y) { return x - y; } } } // using ToodlesFate::a // 将ToodlesFate的命名空间中的a变量从命名空间中放出来 using ToodlesFate::a; // int a = 10; using ToodlesFate::b; // int b = 20; using ToodlesFate::Add; // int Add(int x, int y) {return x + y;} using ToodlesFate::ToodlesFate1::c; // int c = 30; using ToodlesFate::ToodlesFate1::d; // int d = 40; int main() { printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); printf("%d\n", d); printf("%d\n", Add(2, 3)); printf("%d\n", ToodlesFate::ToodlesFate1::sub(2, 3)); return 0; }
// 使用using namespace 将命名空间导入 // 使用 导入命名空间时 必须先导入 外部 再导入内部 #include <cstdio> namespace ToodlesFate { int a = 10; int b = 20; int Add(int x, int y) { return x + y; } namespace ToodlesFate1 { int c = 30; int d = 40; int sub(int x, int y) { return x - y; } } } // 相当于将命名空间中的全部内容展开 using namespace ToodlesFate; using namespace ToodlesFate1; int main() { printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); printf("%d\n", d); printf("%d\n", Add(2, 3)); printf("%d\n", sub(2, 3)); return 0; } // erro namespace ToodlesFate { int a = 10; int b = 20; int Add(int x, int y) { return x + y; } namespace ToodlesFate1 { int c = 30; int d = 40; int sub(int x, int y) { return x - y; } } } using namespace ToodlesFate1; using namespace ToodlesFate; // 也可以如下导入 #include <cstdio> namespace ToodlesFate { int a = 10; int b = 20; int Add(int x, int y) { return x + y; } namespace ToodlesFate1 { int c = 30; int d = 40; int sub(int x, int y) { return x - y; } } } using namespace ToodlesFate::ToodlesFate1; using namespace ToodlesFate;
C++
的输入和输出
c++
使用cin
和cout
来输入和输出。使用cin
和cout
时必须引用头文件iostream
。
cin
:流提取操作符
cout
:流插入操作符// 使用流插入操作符 // endl 是换行 #include <iostream> using namespace std; int main() { char a = 'a'; short b = 10; int c = 30; long d = 40; float e = 3.14f; double f = 23.455; string s = "hello world"; cout << a << " " << b << " " << c << " " << d << " " << e << " " << f << " " << s << endl; return 0; }
// 使用流提取操作符 // cin 读取字符串时 遇到空格停止 // 使用cin cout 好处 不需要控制输入和输出格式 #include <iostream> using namespace std; int main() { char a ; short b; int c; long d; float e; double f; string s; cin >> a >> b >> c >> d >> e >> f >> s; cout << a << " " << b << " " << c << " " << d << " " << e << " " << f << " " << s << endl; return 0; }
缺省参数
概念
缺省参数:在定义函数时,给参数一个默认值,如果没有传进来参数,默认使用这个参数。
缺省参数必须从右到左排列
// 如果函数的声明和定义在同一个文件中 // 可以给声明缺省 或者给定义缺省 或者两个都缺省 两个都缺省时缺省值必须相同 // 定义缺省 #include <iostream> using namespace std; void test(int a, int b); void test(int a , int b = 20) { cout << a << " " << b << endl; } int main() { test(10); return 0; } // 声明缺省 #include <iostream> using namespace std; void test(int a, int b = 20); void test(int a , int b) { cout << a << " " << b << endl; } int main() { test(10); return 0; }
// 如果将声明和定义分开 // 若要定义缺省参数 // 只能在声明中缺省, 不能在定义中缺省 // 因为在预处理时 会将#include 展开 // 在编译过程会进行比对 如果不同的话会报错 // 正确的 // Test.h #pragma once #include <iostream> using namespace std; void Test(int a, int b = 10); // Test.cpp #include "Tets.h" void Test(int a, int b) { cout << a << " " << b << endl; } // main.cpp #include "Tets.h" int main() { Test(1); return 0; } // 错误的 // Test.h #pragma once #include <iostream> using namespace std; void Test(int a, int b); // Test.cpp #include "Tets.h" void Test(int a, int b = 10) { cout << a << " " << b << endl; } // main.cpp #include "Tets.h" int main() { Test(1); return 0; } // 错误的 // Test.h #pragma once #include <iostream> using namespace std; void Test(int a, int b = 10); // Test.cpp #include "Tets.h" void Test(int a, int b = 10) { cout << a << " " << b << endl; } // main.cpp #include "Tets.h" int main() { Test(1); return 0; }
分类
缺省参数类型有两种:
- 全缺省参数
- 半缺省参数
// 全缺省参数 // 没有给实参时,使用默认的参数 // 给了实参之后,从左向右匹配 #include <iostream> using namespace std; void fun(int a = 10, int b = 20, int c = 30) { cout << a << " " << b << " " << c << endl; } int main() { fun(); fun(1); fun(1, 2); fun(1, 2, 3); return 0; }
// 半缺省参数 // 只能从右向左缺省 #include <iostream> using namespace std; // 正确的 void fun(int a, int b, int c = 30) { cout << a << " " << b << " " << c << endl; } // 错误的 //void func(int a = 10, int b = 20, int c) //{ // cout << a << " " << b << " " << c << endl; //} int main() { // 错误的调用 //fun(); //fun(1); fun(1, 2); fun(1, 2, 3); return 0; }
函数重载
函数重载的概念
函数重载:是函数的一种特殊情况,
C++
允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表中参数的个数、参数的类型、参数的顺序不相同,用来处理功能类似的数据类型不同的问题
// 参数的类型不同 #include <iostream> using namespace std; int Add(int a, int b) { cout << "Add(int a, int b)" << endl; return a + b; } double Add(double a, double b) { cout << "Add(double a, double b)" << endl; return a + b; } int main() { cout << Add(2, 3) << endl; cout << Add(2.0, 3.0) << endl; return 0; }
// 参数的个数不同 #include <iostream> using namespace std; int add(int a, int b, int c) { cout << "add(int a, int b, int c)" << endl; return a + b + c; } int add(int a, int b) { cout << "add(int a, int b)" << endl; return a + b; } int main() { cout << add(1, 2) << endl; cout << add(1, 2, 3) << endl; return 0; }
// 参数的顺序不同 #include <iostream> using namespace std; double add(double a, int b) { cout << "add(double a, int b)" << endl; return a + b; } double add(int a, double b) { cout << "add(int a, double b)" << endl; return a + b; } int main() { cout << add(2.1, 3) << endl; cout << add(3, 2.1) << endl; return 0; }
函数重载的原理
cpp
在编译完成后,对函数的名称进行了重新安排,新名字显示了函数的名字,长度,参数类型等编译链接的过程:
- 预处理:头文件的展开、宏替换、条件编译、去掉注释 -> .i
- 编译:检查语法,生成汇编代码 -> .s
- 汇编:把汇编代码转换为二进制的机器码 -> .o
- 链接:找调用函数的地址,链接对应上,合并到一起 -> .out
cpp
对函数的名词进行重新安排,在函数重载后在符号表中对应的字符串不同,所以支持。
c
语言没有对函数的名词进行安排,在函数重载后符号表中对应的字符串相同,所以不支持
C/C++互相调用
调用时调用的是另一文件的静态库,需要进行环境配置,
c
和c++
进行互相调用时,都是对c++
做处理
C
可以调用C/C++
C++
可以调用C/C++
C
调用C
- 先创建一个
c
工程,然后将其改为静态库(点击工程 -> 属性 -> 常规 -> 配置类型 -> 静态库(.lib))- 然后编译生成静态库文件
- 在
c
工程中配置:
- (点击工程 -> 连接器 -> 常规 -> 添加库目录 -> 将编译生成的静态库添加进去(可以在这里找))
- (点击工程 -> 连接器 -> 输入 -> 将静态库名字加进去用分号与其他的分开)
- 包含头文件:头文件要写清楚路径,一直从当前到目标静态库的头文件
C
调用C++
(也是对cpp
进行操作)
先创建一个
cpp
工程,然后将其改为静态库(点击工程 -> 属性 -> 常规 -> 配置类型 -> 静态库(.lib))然后编译生成静态库文件
在
c
工程中配置:
- (点击工程 -> 连接器 -> 常规 -> 添加库目录 -> 将编译生成的静态库添加进去(可以在这里找))
- (点击工程 -> 连接器 -> 输入 -> 将静态库名字加进去用分号与其他的分开)
在
cpp
中进行的操作#ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C #endif EXTERN_C 函数的声明 // __cplusplus 在 cpp 中为真 c 中为假 可以告诉 cpp 编译按照 c 的方式来编译同时不会在c中报告错误
#ifdef __cplusplus extern "C" { #endif // pass 函数 #ifdef __cplusplus } #endif
包含头文件:头文件要写清楚路径,一直从当前到目标静态库的头文件
C++
调用C
- 先创建一个
c
工程,然后将其改为静态库(点击工程 -> 属性 -> 常规 -> 配置类型 -> 静态库(.lib))- 然后编译生成静态库文件
- 在
cpp
工程中配置:
- (点击工程 -> 连接器 -> 常规 -> 添加库目录 -> 将编译生成的静态库添加进去(可以在这里找))
- (点击工程 -> 连接器 -> 输入 -> 将静态库名字加进去用分号与其他的分开)
- 包含头文件:使用
extern "C" {头文件}
头文件要写清楚路径,一直从当前到目标静态库的头文件
C++
调用C++
- 先创建一个
cpp
工程,然后将其改为静态库(点击工程 -> 属性 -> 常规 -> 配置类型 -> 静态库(.lib))- 然后编译生成静态库文件
- 在
cpp
工程中配置:
- (点击工程 -> 连接器 -> 常规 -> 添加库目录 -> 将编译生成的静态库添加进去(可以在这里找))
- (点击工程 -> 连接器 -> 输入 -> 将静态库名字加进去用分号与其他的分开)
- 包含头文件:头文件要写清楚路径,一直从当前到目标静态库的头文件
引用
概念
引用不是新定义一个变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量开辟新的空间,它和它的引用变量共用同一块内存空间。
格式:
类型&引用变量名(对象名) = 引用实体
// 引用的使用 #include <iostream> using namespace std; int main() { int a = 10; int& ra = a; cout << a << " " << ra << endl; a++; cout << a << " " << ra << endl; ra++; cout << a << " " << ra << endl; return 0; }
// 使用引用交换两个变量的位置 #include <iostream> using namespace std; void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } int main() { int a = 10, b = 20; cout << "a = " << a << " b = " << b << endl; swap(a, b); cout << "a = " << a << " b = " << b << endl; return 0; } // 不使用引用交换两个元素的位置 #include <iostream> using namespace std; void swap(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } int main() { int a = 10, b = 20; cout << "a = " << a << " b = " << b << endl; swap(&a, &b); cout << "a = " << a << " b = " << b << endl; return 0; }
特性
引用在定义时必须初始化
// erro #include <iostream> using namespace std; int main() { int& a; return 0; }
一个变量可以有多个引用
#include <iostream> #include <cstdio> using namespace std; int main() { int p = 10; int& rp = p; int& rrp = p; // 打印出来的结果表明 p rp rrp 的值与地址相同 说明一个变量可以有多个引用 cout << p << " " << rp << " " << rrp << " " << endl; printf("%p %p %p", &p, &rp, &rrp); return 0; }
引用一旦引用一个实体,就不能在引用其他实体
#include <iostream> using namespace std; int main() { int a = 10; int& b = a; int c = 20; b = c; // 把c 的值赋值给b 不是把c引用给b cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; return 0; }
常引用
在引用时,权限只能放下,不能将权限扩大。例如:如果一个变量只允许读,不允许修改,则通过引用也只可以读,不能修改;如果一个允许读与写,则创建引用时,可以创建既能读取也能修改或者只能读取不能修改。
#include <iostream> using namespace std; int main() { // 进行对变量引用时 权限只能缩小 不能放大 // 只有读取的权限 const int x = 20; //int& y = x; 有读取 和 修改的权限 权限扩大了 是错误的 // 只有读取的权限 const int& y = x; // 有读取的和修改的权限 int c = 30; // 有读取的和修改权限 int& b = c; // 只有读取的权限 const int& d = c; return 0; }
临时变量具有常属性
#include <iostream> using namespace std; int main() { double a = 2.2; // 将浮点数a的整数部分截取出来 然后拷贝一份 把他赋值给f int f = a; // 引用: // 把浮点数a的整数部分截取出来 然后然后把那个常数的引用赋值给e // 它具有只读性 不能被修改 具有常性 //int& e = a; const int& e = a; return 0; }
引用的应用场所
做参数: 输出型参数能够减少拷贝 提高效率
#include <iostream> using namespace std; void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } int main() { int a = 10; int b = 20; cout << "a = " << a; cout << " b = " << b << endl; swap(a, b); cout << "a = " << a; cout << " b = " << b << endl; return 0; }
做返回值:函数返回后,出了函数的作用域,如果返回对象的空间未还给系统,则可以使用引用返回;如果返回的空间已经还给系统,那么,不能使用引用,需要使用传值返回
// 空间未还给系统 // 可以使用引用返回 #include <iostream> using namespace std; int& count() { static int n = 0; n++; return n; } int main() { for (int i = 0; i < 10; i++) { cout << "第 " << count() << "调用" << endl; } return 0; }
// 空间已经还给系统 // 不可以使用引用返回 // 应该使用拷贝值返回 #include <iostream> using namespace std; int& add(int& a, int& b) { int c = a + b; return c; } int main() { int a = 2; int b = 3; int& ret = add(a, b); // 返回值的空间被还给系统 // 当调用完一次之后 再次访问后被随机值覆盖 // 最好使用传值返回 // 一个常数不能被修改 1 不能用int& 来接受 cout << "add(2, 5)" << ret << endl; cout << "add(2, 5)" << ret << endl; cout << "add(2, 5)" << ret << endl; return 0; }
传值和传引用的对比
值:将值作为参数和返回类型时,在传递参数和返回值的时候,函数不会直接传递实参或者变量本身,而是传递实参或者返回值的一份临时的拷贝。拷贝需要耗时,所以效率低下。
// 传值调用和传引用调用的耗时比较 #include <iostream> #include <time.h> using namespace std; // 定义一个结构体 // 结构体中的变量是 10000 个整型值 struct A { int a[10000]; }; // 使用传值调用 void TestFunc1(A a) {} // 使用传值返回 void TestFunc2(A& a) {} // 测试函数 void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc1(a); } size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) { TestFunc2(a); } size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; } int main() { TestRefAndValue(); return 0; }
引用:将引用作为参数和返回类型时,在传递参数和返回值的时候,函数会直接传递参数或者值本身。没有拷贝,效率比较高。
// 传值返回和传引用返回的比较 #include <iostream> #include <time.h> using namespace std; struct A { int a[10000]; }; // 创建一个全局变量 A a; // 值返回 A TestFunc1() { return a; } // 引用返回 A& TestFunc2() { return a; } void TestReturnByRefOrValue() { // 以值作为函数的返回值类型 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) { TestFunc1(); } size_t end1 = clock(); // 以引用作为函数的返回值类型 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) { TestFunc2(); } size_t end2 = clock(); // 计算两个函数运算完成之后的时间 cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; } int main() { TestReturnByRefOrValue(); return 0; }
引用和指针的对比
- 引用是一个变量的别名,不占空间;指针存放变量的地址,占空间
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化引用一个实体之后,就不能引用其他实体了,而指针可以在任何时候指向任何一个同类型的实体
- 没有
NULL
引用,但有NULL
指针- 在
sizeof
中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数- 引用自加即引用的实体增加
1
,指针自加即指针向后偏移一个类型的大小- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
- 引用和指针在汇编代码的结果是相同的。
// 观察指针和引用的汇编代码是相同的 // 在 return 的上一个句子上打一个断点 // 然后允许到这句后 点右击 转到反汇编 // 观察汇编代码 #include <iostream> using namespace std; int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20; return 0; }
内联函数
概念
用
inline
修饰的函数叫做内联函数。在编译时,
C++
编译器会将在调用内联函数的地方展开,没有函数压栈的开销(类似宏)。查看方式:
在
release
模式下,查看编译器生成的汇编代码中是否存在call Add
// 打断点,调试,查看反汇编 #include <iostream> using namespace std; inline int add(int a, int b) { return a + b; } int main() { int res = add(1, 2); return 0; }
在
debug
模式下,需要对编译器进行设置,否则不会展开(因为debug
模式下,编译器默认不会对代码进
行优化)点击文件 -> 右键 -> 属性 ->
c/c++
-> 常规 -> 改为程序设计库(/zi
) -> 优化 - > 内联函数扩展改为 只使用于__inline(/Ob1)
// 先进行配置 // 然后打断点 调试 转为汇编 #include <iostream> using namespace std; inline int add(int a, int b) { return a + b; } int main() { int res = add(1, 2); return 0; }
特性
inline
是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜
使用作为内联函数// 会展开 #include <iostream> using namespace std; inline void test(int n) { for (int i = 0; i < n; i++); } int main() { test(100); return 0; }
inline
对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline
的函数体内有循环/递归等等,编译器优化时会忽略掉内联// 定义的内连函数会自动忽略 长度太长 #include <iostream> using namespace std; inline void test() { int x = 1; int y = 1; int z = x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; z += x + y; } int main() { test(); return 0; }
// 定义的内联函数会被忽略 使用了递归 #include <iostream> using namespace std; inline void test(int n) { if (n >= 0) test(n - 1); } int main() { test(100); return 0; }
inline
不建议声明和定义分离,分离会导致链接错误。因为inline
被展开,就没有函数地址了,链接就会
找不到// 内联函数不会创建符号表 所以如果分开写 会链接不到 // In.h #include <iostream> using namespace std; inline void f(int i); // In.cpp #include "In.h" void f(int i) { cout << i << endl; } // test.cpp #include "In.h" int main() { f(10); return 0; }
宏:
// 宏和内联函数一样 都是在编译的时候进行代替 #include <iostream> using namespace std; #define ADD(x, y) ((x) + (y)) int main() { cout << ADD(2, 3) << endl; return 0; }
宏和内联函数的区别:
- 宏的优点:
- 增强代码的复用性
- 提高性能
- 宏的缺点:
- 提高性能
- 导致代码可读性差,可维护性差,容易误用
- 没有类型安全的检查
- 内联函数的优点:
- 解决宏函数晦涩难懂,容易写错的问题
- debug支持调试
- 不容写错,就是普通函数的写法
#auto
、for
、nullptr
auto
含义:auto声明的变量必须由编译器在编译时期推导而得
注:
- 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导
auto
的实际类
型- auto并非是一种
类型
的声明,而是一个类型声明时的占位符
,编译器在编译期会将auto
替换为变量实际的类型// 使用auto 推导变量的类型 并显示出来 #include <iostream> using namespace std; short test() { return 3; } int main() { int a = 10; auto b = 'a'; auto c = "hello world"; auto d = 3.1415936; auto f = test(); auto g = { 1, 2, 3 }; auto i = { 'h', 'e', 'l' }; // typeid(a).name() 能够返回a的类型 cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; cout << typeid(f).name() << endl; cout << typeid(g).name() << endl; cout << typeid(i).name() << endl; return 0; }
auto
的使用细则:
auto与指针和引用结合起来使用
用auto声明指针类型时,用
auto
和auto*
没有任何区别,但用auto
声明引用类型时则必须加&
// 当定义指针时 可从下方得到 加不加* 都可以 // 当定义引用时 若不加时 auto 自动推导为那个值的临时拷贝 占用内存 #include <iostream> using namespace std; int main() { int x = 10; auto a = &x; auto* b = &x; auto& c = x; auto e = x; cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(e).name() << endl; cout << x << endl; *a = 20; cout << "*a = 20: " << x << endl; *b = 30; cout << "*b = 30: " << x << endl; c = 40; cout << "c = 40: " << x << endl; e = 50; cout << "e = 50: " << x << endl; return 0; }
在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对
第一个类型进行推导,然后用推导出来的类型定义其他变量#include <iostream> using namespace std; int main() { auto a = 1, b = 2; // 正确的 因为 a 和 b 是同一类型 auto c = 2, d = 4.5; // 错误的 因为 c 和 d 不是同一类型 // int c = 2, d = 4.5 一样 return 0; }
auto
不能使用的场景:
auto不能作为函数的参数(能用作返回,但不建议)
#include <iostream> using namespace std; void test(auto a) {} int main() { test(2); return 0; }
auto不能直接用来声明数组
// auto 声明数组是错误的 #include <iostream> using namespace std; int main() { auto b[] = { 3, 4, 5 }; return 0; }
for
语法
#include <iostream> using namespace std; int main() { int arr[] = { 1, 2, 3, 4 }; // 使用下标 for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { cout << arr[i] << " "; } cout << endl; // 使用指针 for (int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); p++) { cout << *p << " "; } cout << endl; // 范围for // 使用简化 // for循环后的括号由冒号分为两部分,第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围 // 可以用continue来结束本次循环,也可以用break来跳出整个循环 for (int x : arr) { cout << x << " "; } cout << endl; // 使用简化 和自己推导 for (auto x : arr) { cout << x << " "; } cout << endl; return 0; }
范围
for
的使用条件
- for循环迭代的范围必须是确定的(对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围)
- 迭代的对象要实现++和==的操作
nullptr
在
c++
中空指针的三种方法:
NULL
0
(和NULL
等价,在宏中被定义为#define NULL 0
)#ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif
#include <iostream> using namespace std; void f(int) { cout << "f(int)" << endl; } void f(int*) { cout << "f(int*)" << endl; } int main() { // 前两个不同的类型 但产生的相同的结果 f(0); // f(int) f(NULL); // f(int) f((int*)NULL); // f(int*) return 0; }
nullper
:推荐使用注:
- 在使用
nullptr
表示指针空值时,不需要包含头文件,因为nullptr
是C++11
作为新关键字引入的- 在
C++11
中,sizeof(nullptr)
与sizeof((void*)0)
所占的字节数相同- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用
nullptr