这一期时关于C++的内容,从基础开始,大家都加油鸭!!!
文章目录
- 一.命名空间
-
- [1.1 namespace的价值](#1.1 namespace的价值)
- [1.2 namespace的定义](#1.2 namespace的定义)
- [1.3 命名空间使用](#1.3 命名空间使用)
- [二. C++输入和输出](#二. C++输入和输出)
- [三. 缺省参数](#三. 缺省参数)
- [四. 函数重载](#四. 函数重载)
- [五. 引用](#五. 引用)
-
- [5.1 引用的概念](#5.1 引用的概念)
- [5.2 引用的特性](#5.2 引用的特性)
- [5.3 引用的使用](#5.3 引用的使用)
- [5.4 越界问题](#5.4 越界问题)
- [5.5 const引用](#5.5 const引用)
-
- [4. const引用的方便之处:](#4. const引用的方便之处:)
- [5.6 ==引用和指针的区别==](#5.6 ==引用和指针的区别==)
一.命名空间
1.1 namespace的价值
在C++里也有很多的变量(对象),函数和类。如果(变量,函数和类)的名称全部在全局作用域中,会产生冲突(比如在一个大型项目中,两个程序员分别完成两个部分的内容,有两个名称相同的变量名称,到底使用哪一个呢?)
使用命名空间的目的是对标识符的名称进行本地化,以避免(命名)冲突或名字污染,namespace
关键字的出现就是针对这种问题的。
1.2 namespace的定义
- 定义命名空间:namespace 空间的名字,后面再是花括号{}。和函数有点类似。
- 同一个域不能用同一个变量名,不同域可以用同一个变量名。
- namespace本质是定义一个域
- C++中域有函数局部域,全局域,命名空间域,类域(4个)。域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑[默认是在局部域main查找,之后会去全局域查找,不会主动去命名空间找],所以有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑 ,还会影响变量的生命周期 ,命名空间域和类域不影响变量生命周期,它们只是利用域做了隔离(在main函数里还可以用命名空间hou里面的变量,hou::rand)。
cpp
#include<stdio.h>
#include<stdlib.h> //注意,在这个库里有rand函数,在没有说明的情况下调用的是全局域里的
namespace hou
{
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
//调用命名空间里的函数
int a = hou::Add(1,2);
//原本是Add,现在是hou::Add(1,2)
//调用命名空间里的结构体
struct hou::Node bian;
//原本是struct Node bian; 注意hou::的位置
}
- namespace只能定义在全局,不能在main函数或者什么局部域里面定义。namespace只是为了和全局的内容做隔离,而局部域和全局的内容天然就是隔离的。
- namespace可以嵌套定义
cpp
namespace hou
{
namespace rong
{
int rand = 10;
}
namespace ning
{
int rand = 90;
}
}
int main()
{
printf("%d\n", hou::ning::rand);
}
- 项目工程中多文件中定义的同名namespace会认为是一个namespace,不会冲突。
- C++标准库都放在一个叫std(standard)的命名空间中。
1.3 命名空间使用
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以我们要使用命名空间中定义的变量/函数,有三种方式:
- 指定命名空间访问(推荐推荐推荐)
cpp
//比如 hou::rand
- using将命名空间中的全部成员展开(但不推荐这种方式,会有命名冲突的风险)
cpp
using namespace hou;
//这样的话,找rand不仅会在全局域找,还会在hou里面找
- using将命名空间中某个成员展开(但不推荐这种方式,会有命名冲突的风险)
cpp
using hou::rand;
二. C++输入和输出
- iostream是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。
- std::cin 是 istream 类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。(c是字符的意思)【只有在内存当中才有整型,浮点型,字符,原反补,而在其他设备:如文件,网络,终端控制台等等只认识字符】
- std::cout 是 ostream 类的对象,它主要面向窄字符的标准输出流。
- std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。(endl----->end line结束行)
- <<是流插入 运算符(<< 这个全名是流插入操作符,作用就是向控制台打印),>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)【可以自动识别变量类型,什么意思呢?就是不需要再写%d,%s之类的,<<可以自动识别】
- cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
cpp
#include<iostream>
int main()
{
std::cout << "hello world!"; //cout标准输出流
//如果想换行的话
std::cout << "hello world!"<<std::endl; //C++推荐这个方式
std::cout << "hello world!" << ' \n'; //可以连续的输出流插入,类型甚至可以不一样
}
- 这里我们没有包含<stdio.h>,也可以使用printf和scanf,在包含iostream时间接包含了。vs系列编译器是这样的,其他编译器可能会报错。
三. 缺省参数
-
"缺省参数"又叫做"默认参数",有参数,使用指定的参数;没有参数,使用缺省参数。(C语言没有缺省参数)
-
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。
-
缺省参数分为全缺省和半缺省参数。
-全缺省:全部形参给缺省值(即形参一个都没有,都使用默认值)。
-半缺省:传了部分形参,但还有一部分形参使用缺省值(默认值)。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
cpp
void function(int a=1, int b=2, int c=3)
{
printf("%d %d %d\n", a, b, c);
}
int main()
{
function(); //全缺省(全部形参使用缺省值)
function(11); //半缺省(没有传部分形参)
//function(,22,)这个是错误的,半缺省参数必须从右往左依次连续缺省
}
- 函数声明和定义分离时 ,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。 (声明是在.h(即目录)里的那个(
void function(ST* ps,int a = 10);
),定义是在.cpp的那个void function(ST* ps,int a){}
四. 函数重载
C++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同。(C语言不支持)
- 参数个数不同。
- 参数类型不同。
- 参数顺序不同。(本质还是参数类型不同)
- '返回值不同'不能作为重载条件,因为调用时也无法区分,不知道该调用哪一个。
cpp
// 1、参数类型个数不同
void f()
{
///
}
void f(int a)
{
///
}
f();
f(12);
cpp
// 2、参数类型不同
int Add(int left, int right)
{
///
}
double Add(double left, double right)
{
}
//调用时:
Add(10,20);
Add(1.1,2.2);
cpp
void f(int a, char b)
{
//
}
void f(char b, int a)
{
}
f(10,'m');
f('m',10);
错误:
cpp
// f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
//语法上不存在错误,但调用时会有问题
void f1()
{
/
}
void f1(int a = 10)
{
//
}
f1(); //有可能是第一个,也有可能时第二个,使用缺省值
五. 引用
5.1 引用的概念
引用:给已经存在的变量重起一个别名 。(编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间)
类型& 引用别名 = 引用对象;
cpp
int a = 10;
int& b = a; //给a起一个别名b
区分typedef 和引用
typedef:给类型起别名
引用:给变量起别名
5.2 引用的特性
- 引用的对象必须是初始化过的。(引用在定义是必须初始化)
- 一个变量可以有多个引用。(可以有多个别名)
- 引用一旦引用一个实体,再不能引用其他实体。(专属别名)
5.3 引用的使用
引用的好处:
减少拷贝------>(1)提高效率;(2)改变引用对象(小号)的同时,改变被引用对象(本人)
引用--->没有开空间--->减少拷贝---->效率
- 引用传参:引用传参跟指针传参功能是类似的。
以前想改变a,b的值,需要把a和b的地址传过去,通过地址改变它们的值。
现在不需要这么麻烦,可以直接给a和b取个别名,将形参写成别名,改变别名即改变本人。
指针传参
引用传参
cpp
void change(int& aa,int&bb)
{ //aa是a的别名,bb是b的别名
//aa是引用对象,a是被引用的对象
aa = 1;
bb = 9;
}
int main()
{
int a = 10;
int b = 90;
change(a,b); //以a举例:a传递给形参int& aa的时候, 这个aa就是a的引用
std::cout << a << " " << b;
}
- 引用返回值
STTop()是一个返回值为int类型的函数,想让函数的返回值++,不能直接写STTop()++。
5.4 越界问题
越界访问不一定报错。
- 越界读,不报错。【比如数组a里有10个元素,cout<<a[10];就是越界读】
- 越界写,不一定报错。(越界写是以抽查的方式,越界的地方放一个值,比如-1,如果被改的话,就是越界写)【比如数组a里有10个元素,a[10]=33;就是越界写】
5.5 const引用
cpp
const int a = 10;
int& ra = a;
1.大家注意,这个方式是不可以的哈。可以引用const对象,但是必须也用const引用。
cpp
const int a = 10;
const int& ra = a;
2.const可以引用const对象,也可以引用普通对象。(对象的访问权限 在引用过程中可以缩小,但是不能放大。对象是const int代表着这个a值不能被修改,那么你的别名一定也没有修改的权限)
cpp
//权限可以缩小
int b = 1; //b可以被修改
const int& rb = b; //b的别名rb,它的权限缩小了,不能被修改
//整个过程只影响rb的权限,不影响b的权限
cpp
//权限不可以被放大
//这个代码是不可以的,pa(即a的地址)不能被修改
//pb(也想存储a的地址),怎么可以让它是int*类型可以被修改呢
const int a = 10;
const int* pa = &a;
int* pb = pa;
const在*之前const int* pa=&a;
,修饰的是变量的内容。在 * 之后int* const pa=&a;
,修饰的是变量本身.
cpp
//错误示范
int a = 10;
const int* pa = &a; //pa的内容(a的地址)不能被修改
int* pb = pa;
//正确示范
int b = 99;
int* const pb = &b; //pb不能被修改(而不是pa指代的内容不能改),即pa里面存的内容一定b的地址
int* pbb = pb; //pb里放的内容和pa一样,但pb里的内容可以变
cpp
int a = 9;
int& rb = a * 3;
首先先了解一下什么是临时对象?
临时对象就是编译器需要一个空间去暂存(表达式的求值结果)时,所临时创建的一个未命名的对象,C++中把这个未命名对象叫做临时对象
a*3的值会先保存在临时对象里面,而C++规定临时对象具有常性(就像被const修饰一样),那么
cpp
int a = 9;
//int& rb = a * 3;
//表达式a*3被保存在临时变量里,被const修饰。而rb是int&,没有被const修饰。本质是权限变大的问题
//应该改成
const int& rb=a*3;
cpp
double a = 12.34;
const int& ra = a;
4. const引用的方便之处:
可以引用普通对象,const对象,临时对象
cpp
void func(const int& cc)
{
//将参数写做const int&是很方便的,调用函数时,既可以是int(缩小权限)
//又可以是const int&(权限没变)
//还可以是表达式 或者 double
}
int main()
{
int a = 0;
func(a);
const int b = 9;
func(b);
func(a * 3);
double d = 2.2;
func(d);
}
现在引用的都是比较小的参数,假如是4000多自己,传参的话需要临时拷贝,需要大量空间,这时引用就很厉害了。
5.6 引用和指针的区别
各有自己的特点,互相不可替代
- 引用是一个变量的取别名不开空间,指针是存储一个变量地址,要开空间
- 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
- 引用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
- 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
- sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间(地址)所占字节个数(32位平台下占4个字节,64位下是8byte)
- 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些