目录
C++的三个大版本:C++98,C++11,C++20
一、C++的第一个程序:
用C++写hello world,cout是标准输出流,输出到终端,endl是换行作用,这里先简单的看一下C++的语法格式,后面详细说明。
#include<iostream>
using namespace std;
int main()
{
cout << "Hello world" << endl;
return 0;
}
二、命名空间namespace:
1.namespace的定义:
1.定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。 2.namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量。 3.namespace只能定义在全局,当然他还可以嵌套定义。 4.项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。 5.C++标准库都放在⼀个叫std(standard)的命名空间中。 ::是域作用限定符 如下代码:namespace命名空间内为一个独立的域,与全局域分开#include<stdio.h>
#include<stdlib.h>
namespace test
{
int rand = 20;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
printf("%p\n", rand);
printf("%d\n", test::rand);
return 0;
}
测试结果:
2.namespace的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以 下⾯程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式: 1.指定命名空间访问,项目中推荐这种方式。 2.using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。 3.展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。 如下代码演示:1.
namespace test
{
int a = 0;
int b = 1;
}
int main()
{
printf("%d", test::a);//指定命名空间访问
return 0;
}
2.
namespace test
{
int a = 0;
int b = 1;
}
using test::a;//using将命名空间中某个成员展开
int main()
{
printf("%d", a);
return 0;
}
3.
namespace test
{
int a = 0;
int b = 1;
}
using namespace test;//展开命名空间中全部成员
int main()
{
printf("%d", a);
printf("%d", b);
return 0;
}
三、C++输入与输出
1.<iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。 |
2.std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输⼊流。 |
3.std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。 |
4.std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。 |
5.<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)。 |
6.使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊输出可以⾃动识别变量类型,其实最重要的是 C++的流能更好的⽀持⾃定义类型对象的输⼊输出。 |
7.IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识。 |
8.cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使⽤⽅式去⽤他们。 |
9.⼀般⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std。 |
10.这⾥我们没有包含<stdio.h>,也可以使⽤printf和scanf,在包含<iostream>间接包含了。vs系列编译器是这样的,其他编译器可能会报错。 |
演示代码如下:
#include<iostream>
using namespace std;
int main()
{
int a = 0;
double b = 0.1;
char c = 'x';
cout << a << " " << b << " " << c << endl;
/*scanf("%d%lf", &a, &b);
printf("%d %lf\n", a, b);*/
cin >> a;
cin >> b >> c;
cout << a << endl;
cout << b << " " << c << endl;
return 0;
}
输入与输出结果:
四、缺省参数
定义与注意:
1.缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为
全缺省和半缺省参数
。(有些地⽅把缺省参数也叫默认参数)
2.全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须
从右往左依次连续缺省
,不能间隔跳跃给缺省值。
3.带缺省参数的函数调⽤,C++规定必须
从左到右
依次给实参,不能跳跃给实参。
4.函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须
函数声明
给缺省
值
例:
#include <iostream>
#include <assert.h>
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(); // 没有传参时,使⽤参数的默认值
Func(10); // 传参时,使⽤指定的实参
return 0;
}
#include <iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1,2);
Func1(1,2,3);
Func2(100);
Func2(100, 200);
Func2(100, 200, 300);
return 0;
}
五、函数重载
C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。 例如:int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
上面的两个函数形参不同(个数或者类型或者顺序不同),构成重载函数,同名但在调用时编译器就会根据输入的实参类型进行调用
注意:空形参函数无法与缺省函数构成重载函数,例如:
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}
在调用时编译器无法判别该调用哪个函数
六、引用
1.引用的概念
引用是对一个变量起别名,本质上还是被引用变量
类型& 引⽤别名 = 引⽤对象 例如:using namespace std;
int main()
{
int a = 1;
int& b = a;
int& c = b;
cout << &a << "\n" << &b <<"\n" << &c << endl;
return 0;
}
结果:
地址相同,说明引用后的变量是被引用变量本身
2.引用的特性
1. 引用 在定义时 必须初始化
2.⼀个变量可以 有多个引用
3.引用⼀旦引用⼀个实体,再不能引用其他实体
以上三点尤其重要
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int c = 0;
int& b = a;
b = c;
cout << b << " " << a << endl;
return 0;
}
这里的b=c不是改变引用 而是将c的值赋给b,a值也随之改变
在单链表设计中如果形参改为引用的话就不用传二级指针了,原来传二级指针是为了修改头节点,形参是对实参的临时拷贝,形参改为引用后实参会随形参的改变而改变(引用的底层是指针,本质上也是传址)
3.const引用
1.可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。 |
2.不需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场 景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。 |
3.所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。 |
在类型变化和值为算式时系统会需要⼀个空间暂存表达式的求值结果,即临时对象,需要使用const常量修饰变量储存结果。
#include<iostream>
using namespace std;
int main()
{
int a = 10;
const int& ra = 30;
//算式表达式
// 编译报错: “初始化”: ⽆法从“int”转换为“int &”
// int& rb = a * 3;
const int& rb = a*3;
//类型转换
double d = 12.34;
// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
// int& rd = d;
const int& rd = d;
return 0;
}
关于权限的说明:
// 这⾥的引⽤是对b访问权限的放大
const int b = 20;
int& rb = b;
// 这⾥的引⽤是对b访问权限的缩⼩
int b = 20;
const int& rb = b;
权限可以缩小但是不能放大 ,否则会编译报错
4.引用和指针的关系
C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。 1.语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。 2.引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。 3.引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。 4.引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。 5.sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte) 6.指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。六、inline
1.概念与使用事项:
1.⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。
2.inline对编译器只是一个建议,inline更适用于频繁调用的短小函数,而像递归函数,代码多的函数,加上inline也会被编译器忽略。
3.C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调 试,C++设计了inline⽬的就是替代C的宏函数。 4.vs编译器 debug版本下⾯默认是不展开inline的,这样方便调试,debug版本想展开需要设置⼀下 以下两个地⽅。 5.inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地 址,链接时会出现报错。 函数是一个可以重复使用的代码块,CPU会一条条挨着执行其中的一代码,CPU在执行主调函数代码时如果遇到被调函数,主调函数会暂停,CPU转而开辟一份重新的空间进行压栈出栈等,执行被调函数的代码,完毕后返回主调函数