文章目录
-
- [一. 函数重载](#一. 函数重载)
- 二.引用
-
- [2.1 概念和定义](#2.1 概念和定义)
- [2.2 引用的特性](#2.2 引用的特性)
- [2.3 引用的使用](#2.3 引用的使用)
- [2.4 const引用](#2.4 const引用)
- [2.5 指针和引用的关系](#2.5 指针和引用的关系)
- [三 inline](#三 inline)
- [四. nullptr](#四. nullptr)
一. 函数重载
在C语言中,我们不支持函数重载,但在C++中,可以进行函数重载。
简单来说,C++支持在同一作用域出现同名函数,但要求同名函数的形参不同,可以是类型不同或者个数不同。
cpp
//1. 形参类型不同
int Add(int left,int right)
{
cout<<"Add(int left,int right)"<<endl;
return left+right;
}
double Add(double left,double right)
{
cout<<"Add(double left,double right)"<<endl;
return left+right;
}
int main()
{
int ret1=Add(3,5);
cout<<ret1<<endl;
double ret2=Add(3.0,5.0);
cout<<fixed<<setprecision(1)<<ret2<<endl;
return 0;
}
cpp
//2.形参个数不同
void fuc()
{
cout<<"fuc()"<<endl;
}
void fuc(int a)
{
cout<<"fuc(int a)"<<endl;
}
int main()
{
fuc();
fuc(1);
return 0;
}
cpp
//3.参数类型顺序不同(本质上还是形参类型不同)
void fuc(int a,char b)
{
cout<<"fuc(int a,char b)"<<endl;
}
void fuc(char b,int a)
{
cout<<"fuc(char b ,int a)"<<endl;
}
int main()
{
fuc(1,'0');
fuc('0',1);
return 0;
}
cpp
//4.要特别注意的是,返回类型不同不能作为重载的条件
int fuc(int a)
{
return 0;
}
void fuc(int a)
{
}

可以看见,编译器报错,无法进行重载。
另外,我们要注意无参和缺省参数的使用。
cpp
//此外,这两个函数会构成重载,但编译器无法区分要调用那个,仍然会报错
void fuc()
{
cout<<"fuc()"<<endl;
}
void fuc(int a=0)
{
cout<<"fuc(int a)"<<endl;
}
int main()
{
fuc();
}

可以看见,编译器无法区分要去调用那个函数,报错。
二.引用
2.1 概念和定义
引用并不是去定义一个新的变量,而是给一个已经存在的变量取一个别名,编译器不会去给这个引用变量去开辟新的空间,它和它引用的变量是同一块内存地址。
类型 & 引用别名=引用对象
cpp
#include<iostream>
using namespace std;
int main()
{
int a=10;
int &b=a;//给a取了一个别名叫做b
int &c=b;//给a的别名b再取一个别名叫c
cout<<&a<<endl;
cout<<&b<<endl;
cout<<&c<<endl;
return 0;
}

我们可以看见abc的地址都是一样的,证明编译器并未有为引用变量再去开新的空间。
2.2 引用的特性
- 引用在定义的时候必须要初始化。
- 一个变量可以有多个引用。
- 引用一旦引用了一个实体,就不可以再去引用别的对象。
cpp
#include<iostream>
using namespace std;
int main()
{
int a=10;
// int &b; 不能这样写,引用必须要进行初始化
// b=a;
int &b=a;
int &c=a;//一个变量可以有多个引用
int d=20;
d=b;//这里并不是引用 而是赋值 引用一旦确定了对象,就不可以再去修改
cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
cout<<d<<endl;
return 0;
}
2.3 引用的使用
- 引用在实践中主要是用于引用传参和引用做返回值来减少拷贝提高效率和改变引用对象时同时改变被引用对象。
- 引用传参和指针传参功能类似,但引用传参更方便一些。
- 引用和指针在实践中相辅相成,功能有重叠性,但各有特点,互相不可替代。C++的引用定义之后不可以改变指向,如在定义树和链表时不可以使用引用,只能使用指针。
cpp
#include<iostream>
using namespace std;
//指针实现交换
void swap(int *pa,int *pb)
{
int tmp=*pa;
*pa=*pb;
*pb=tmp;
}
//引用实现交换
void swap(int &rc,int&rd)
{
int tmp=rc;
rc=rd;
rd=tmp;
}
int main()
{
int a=10;
int b=20;
swap(&a,&b);
cout<<a<<" "<<b<<endl;
int c=30;
int d=40;
swap(c,d);
cout<<c<<" "<<d<<endl;
return 0;
}
引用在栈中的应用
cpp
#include<iostream>
using namespace std;
typedef int DataType;
typedef struct Stack
{
DataType* arr;//数组
int top;//栈顶
int capacity;//容量
}Stack;
//引用
void STInit(Stack&st)
{
st.arr = NULL;
st.top = 0;
st.capacity = 0;
}
//销毁
void STDestroy(Stack& st)
{
st.arr = NULL;
st.top = 0;
st.capacity = 0;
}
//插入数据
void STPush(Stack&st, DataType x)
{
if (st.top == st.capacity)
{
int newcapacity = st.capacity == 0 ? 4 : 2 * st.capacity;
DataType* tmp = (DataType*)realloc(st.arr, sizeof(DataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc");
exit(01);
}
st.arr = tmp;
st.capacity = newcapacity;
}
st.arr[st.top]=x;
st.top++;
}
//判空
bool STEmpty(Stack&st)
{
return st.top == 0;
}
//删除数据
void STPop(Stack&st)
{
st.top--;
}
//取栈顶元素
DataType STTop(Stack&st)
{
return st.arr[st.top - 1];
}
//获取栈中有效个数
int STSize(Stack& st)
{
return st.top;
}
void test01()
{
Stack st;
STInit(st);
STPush(st, 1);
STPush(st, 2);
STPush(st, 3);
STPush(st, 4);
STPush(st, 5);
int size = STSize(st);
printf("size=%d\n", size);
while (!STEmpty(st))
{
//printf("%d ",st.arr[st.top-1]);//st不是指针不能使用->
//STPop(&st);
DataType top = STTop(st);
printf("%d ", top);
STPop(st);
}
/*STPop(&st);
STPop(&st);*/
STDestroy(st);
}
int main()
{
test01();
return 0;
2.4 const引用
- 可以引用一个const对象,但必须时const引用。const引用可以引用普通对象,因为对象的访问权限可以被缩小,但不可以放大。
- 要注意
int a=10;double b=a;const double &c=a这样的情况会将值保存在临时对象中,C++规定,临时对象具有常性,因此要用常引用。
cpp
int main()
{
const int a=10;
//int &b=a;这样写是权限的放大
//a本身是不可以被修改的 但b作为a的引用却可以修改 权限放大
const int &b=a;
//这样写没问题
//要注意的是权限不能放大,但可以缩小
int c=20;
const int& d=c;//权限的缩小
//d++;无法这样写,因为d不可修改
return 0;
}
cpp
int main()
{
int a=10;
double b=a;//隐式转换 相当于将a的值存在临时变量中再转换给d 临时变量具有常性 不可被修改
//double &c=a; 会报错 产生的临时变量具有常性 相当于权限的放大
const double &c=a;//这样写没问题
return 0;
}
2.5 指针和引用的关系
- 引用在使用的时候必须初始化,指针建议初始化,不强制。
- 引用在使用的时候必须初始化,指针建议初始化,不强制。
引用是给一个已经开了空间的变量取别名,自己本身不开空间,指针存储变量的地址,必须开空间。 - 引用在初始化一个对象后,不可以更改,指针无限制。
- 引用可以直接访问指向对象,指针则要解引用。
- sizeof中含义不同,引用时则为引用类型的大小,而指针的大小则是固定的,(32位下为8,64位下为4)。
- 指针很容易出现野指针和空指针的问题,引用则不会。
三 inline
- 用inline修饰的函数叫做内联函数,编译时会在调用的时候将其展开,这样调用内联函数的时候就需要建立函数栈帧了,可以提高效率。
- inlien只是一个建议,当你写的inline函数不宜使用,编译器会将其忽略。
- C++中inline是用来代替define存在
- inline不建议将声明和定义分开到两个文件,分离会导致编译错误。我们在使用inline函数时,直接将其定义到.h文件中。
cpp
inline int Add(int a,int b)
{
return a+b;
}
int main()
{
int ret=Add(1,2);
cout<<ret<<endl;
return 0;
}
cpp
//当我们要写一个宏来实现Add函数时,要如何写呢?
#define ADD(a,b) ((a)+(b))
//注意不能加分号 因为define相当于替代 假设 int ret=Add(1,2); 会变成((1)+(2));;
//加外面的括号是为了保持整体的优先级 如Add(1,2)*2 的时候 不加外面的括号会让2*2先运算
//加里面的括号是为了保证参数本身的优先级 如Add(1|2,3&4) 不加里面的括号会变成 (1|2+3&4)
//由于+的优先级更高,会先算2+3
四. nullptr
- C++中NULL可能被定为字面常量0,或者C中被定义为无类型指针(void*)的常量。
- C++11中引入nullptr,nullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量,它可以转换为任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式的转换为指针类型,不能被转换为整数类型。
cpp
void f(int x)
{
cout<<"f(int x)"<<endl;
}
void f(int*x)
{
cout<<"f(int *ptr)"<<endl;
}
int main()
{
f(0);
//f(NULL); //本意是调用指针类型的函数,但编译器将NULL定义为了0,调用了f(int x)的函数
// f((int*)NULL);//找不到任何一个相匹配的函数
f(nullptr);
return 0;
}