打字不易,留个赞再走吧~~~
目录
一函数重载
C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者
类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同
名函数的。
下面三种情况1、参数类型不同
2、参数个数不同
3、参数类型顺序不同
这些条件都可以作为函数重载的条件,不影响结果,而且输入的参数还可以自动识别类型。
c
#include<iostream>
using namespace std;
// 1、参数类型不同
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;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
有两种情况不能进行函数重载
1 返回值不同不能作为重载条件,因为调⽤时也⽆法区分
2缺省参数和无参的函数不能 同时定义
c
// 下⾯两个函数构成重载
// f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}
二引用
1 引⽤的概念和定义
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,
它和它引⽤的变量共⽤同⼀块内存空间。
具体表示为:类型& 引⽤别名 = 引⽤对象;
C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<< 和 >>,这⾥引⽤也和取
地址使⽤了同⼀个符号&,⼤家注意使⽤⽅法⻆度区分就可以。
c
#include<iostream>
using namespace std;
int main()
{
int a = 0;
// 引⽤:b和c是a的别名
int& b = a;
int& c = a;
// 也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
这里看到他们的空间都是一块地方有就是全部a,b,c,d都指向一块空间
2引⽤的特性
引用有三个特性
• 引⽤在定义时必须初始化
• ⼀个变量可以有多个引⽤
• 引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
引⽤在定义时必须初始化
在指针定义时候我们可以int *p;
然后给p赋值。
但是在引用我们必须给引用的值给予一个被引用对象
即int &a=b
给b取别名为a,他们指向同一块空间
⼀个变量可以有多个引⽤
当然我们可以给b取多个别名,即引用
int &a=b;
int &c=b;
int &d=b;
引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
我们上面吧b引用给a了
那么a就不能引用其他值了
3引⽤的使⽤
•引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被
引⽤对象。
• 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
• 引⽤返回值的场景相对⽐较复杂,我们在这⾥简单讲了⼀下场景,还有⼀些内容后续类和对象章节
中会继续深⼊讲解。
• 引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他
语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的点,C++引⽤定义后不能改变指向,
Java的引⽤可以改变指向。
• ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开
复杂的指针,但是很多同学没学过引⽤,导致⼀头雾⽔。
例如下面的代码我们要写swap函数,我们用指针也能完成,但是我们把实参写成引用的形式,看似形参能改变实参了,其实是其引用的值改变实参的。而且这样跟指针相比少开辟了空间,减少了消耗。
c
void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}
int main()
{
int x = 0, y = 1;
cout << x <<" " << y << endl;
Swap(x, y);
cout << x << " " << y << endl;
return 0;
}
三inline
⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联
函数就不需要建⽴栈帧了,就可以提⾼效率。
• inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展
开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁
调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
在定义函数返回值前我们加上inline的函数被称为内联函数,我们可以理解为在代码执行的时候会生成.exe可执行文件,里面有着我们写的指令,如果是内联函数我们在函数调调用时候会展开函数具体方法,不用使用函数开辟的栈帧。
但是如果函数语句过多,比如是个循环或者是递归语句,我们每次展开的空间过大,编译器会自动不展开,开辟函数栈帧,然后调用。
默认编译器在debug版本是不会展开的,要在编译器设置,而且就算设置了,语句过长,也会不展开,算是编译器的保护机制,防止程序员写出代码不好,占用空间太大。
四nullptr
NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码:
c
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
• C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void*)的常量。不论采取何种
定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的
f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);
调⽤会报错。
可以看出在c++中null就是0,c语言中则是强制转换为void* 的0.
c
#include<iostream>
using namespace std;
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
f(nullptr);
return 0;
}
可以看出如果引入nullptr,它不仅可以起到空指针的效果,它还可以自动识别类型,不然每次根据函数方法强转很麻烦。
• C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换
成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被
隐式地转换为指针类型,⽽不能被转换为整数类型。