从C语言到C++②(第一章_C++入门_篇)缺省参数+函数重载+引用
目录
1.缺省参数
先看一个简单的函数,功能就是打印出传递过来的数字:
-
-
using namespace std;
-
-
void Func(int num)
-
{ // 此时接收,num = 1
-
cout << num << endl;
-
}
-
-
int main()
-
{
-
Func(1); // 传参:1
-
-
return 0;
-
}
如果我不想传参呢?我想直接调用 Func 函数:
-
-
using namespace std;
-
-
void Func(int a)
-
{
-
cout << a << endl;
-
}
-
-
int main()
-
{
-
Func();//此时就会报错
-
-
return 0;
-
}
因为没有传递参数,所以自然会引发报错。
不过,在C 里我们可以利用一个叫 "缺省参数" 的东西,
让该函数可以做到不传参也能运行的效果。
-
-
using namespace std;
-
-
void Func(int a = 0)
-
{
-
cout << a << endl;
-
}
-
-
int main()
-
{
-
Func();//成功打印0
-
-
return 0;
-
}
下面我们就将学习这个神奇的 "缺省参数" 。
1.1缺省参数概念
1.2缺省参数的使用:
-
-
using namespace std;
-
-
void Func(int a = 0) // 缺省值作为形参,传给 a
-
{
-
cout << a << endl;
-
}
-
-
int main()
-
{
-
Func(10); // 传参时:使用指定的实参(传入10)
-
Func(); // 没有传参时,使用默认的参数值(默认值:0)。
-
-
return 0;
-
}
① 第一次调用 Func 时,指定了实参,就会照常传入,这里指定的是 10,所以传过去的是 10。
② 第二次调用 Func 时,并没有指定实参,所以进入函数后,
形参 a 会取缺省值 0 作为参数的值。
③ 因此,第一次打印的结果是 10,第二次打印的结果是 0。
注意:
① 声明不能在 .h 和 .cpp 里同时出现缺省参数,要么声明里写,要么在定义里写!
② 缺省值必须是常量或全局变量。
② 缺省参数C 里面的,C语言不支持(编译器不支持)。
1.3.1 全缺省参数
必须所有参数都带有缺省值,才能叫作全缺省参数。
代码演示:
-
-
using namespace std;
-
-
void Func(int a = 10, int b = 20, int c = 30)、
-
{
-
printf("%d %d %d\n", a, b, c);
-
}
-
-
int main()
-
{
-
Func(); // 不穿,一个都没传
-
Func(1); // 只传了一个
-
Func(1, 2); // 传了两个,但没完全传
-
Func(1, 2, 3); // 全都传了,就没缺省参数什么事了
-
-
return 0;
-
}
打印:
10 20 30
1 20 30
1 2 30
1 2 3
解析:
① 第一次调用 Func 时,什么都没有传,所以结果直接就采用默认值。
② 第二次调用 Func 时,只传了一个参数,那么结果只有 a 不是默认值。
③ 第三次调用 Func 时,传了两个参数,那么结果只有 c 会是默认值了。
④ 最后一次调用 Func 时,所有参数都传了,那么结果都不会是默认值。
只想传中间的b可以吗?:
-
-
using namespace std;
-
-
void Func(int a = 10, int b = 20, int c = 30)
-
{
-
printf("%d %d %d\n", a, b, c);
-
}
-
-
int main()
-
{
-
Func(, 2,);//错误
-
-
return 0;
-
}
不可以!
参数的传递按照语法是从左往右传递的,因为这是语法定死的,所以没有办法传。
1.3.2 半缺省参数
半缺省参数:函数中的所有参数从右往左连续地缺省一部分
这一部分可以是多个参数,也可以是一个参数(一个也算一部分),
但是它们必须是 "连续地" 。参数传递顺序根据根据函数调用约定。
注意事项:
① 半缺省并不是缺省一半,而是缺省一部分。
② 半缺省参数必须从右往左缺省,且必须是连续地。即,必须从右往左连续缺省。
吐槽:既然不是缺省一半,还叫半缺省参数,这合理吗?这不合理!(小声)
这个 "半" 字确实用的不合理,倒不如叫 "部分缺省参数" ,会显得更加合理一些。
-
-
using namespace std;
-
-
// 从左往右 "连续地"
-
void Func(int a, int b, int c = 30)
-
{
-
printf("%d %d %d\n", a, b, c);
-
}
-
/* 半缺省:从右往左连续地缺省一部分参数
-
a - 必须传 (因为没缺省)
-
b - 必须传 (因为没缺省)
-
c - 可传可不传 (因为缺省了)
-
*/
-
-
int main(void)
-
{
-
Func(1, 2); // a b 没缺省,所以必须要传,c缺省了所以可以不传
-
Func(1, 2, 3); // 都传
-
-
return 0;
-
}
1.4缺省参数的应用场景
缺省参数的运用场景有很多,我们随便举个例子。
我们在学习数据结构时,实现顺序表、栈时定义容量 capacity 时,默认值我们当时推荐的是给 4,这里就可以设置缺省值:
-
typedef struct Stack
-
{
-
int* array;
-
int top;
-
int capacity;
-
} Stack;
-
-
void StackInit (Stack* pst, int capacity = 4 ) // 设置缺省值为4(默认容量为4)
-
{
-
pst->array = (int*)malloc(sizeof(int) * capacity);
-
pst->top = 0;
-
pst->capacity = capacity;
-
}
-
-
-
int main()
-
{
-
Stack st;
-
StackInit(&st); // 不知道栈最多存多少数据,就用缺省值初始化
-
StackInit(&st, 100); // 知道栈最多存100数据,显示传值。这样可以减少增容次数。
-
-
return 0;
-
}
2. 函数重载
2.1函数重载的概念
函数重载常用来处理实现功能类似数据类型不同的问题。
函数重载:C 允许在同一个作用域中存在同名的函数。
下面三个不同只要满足一个不同,就可以触发函数重载:
① 参数类型不同
② 参数个数不同
③ 参数顺序不同
-
-
using namespace std;
-
-
int Add(int x, int y)
-
{
-
cout << "int Add:" << endl; // 为了方便区分
-
return x y;
-
}
-
-
double Add(double x, double y)
-
{
-
cout << "double Add:" << endl; // 为了方便区分
-
return x y;
-
}
-
-
int main()
-
{
-
cout << Add(1, 2) << endl;
-
cout << Add(1.1, 2.2) << endl;
-
return 0;
-
}
打印输出:
② 参数个数不同
-
-
using namespace std;
-
-
void Func(int a)
-
{
-
cout << "Func(int a)" << endl;
-
}
-
-
void Func(char b, int a)
-
{
-
cout << "Func(char b, int a)" << endl;
-
}
-
int main()
-
{
-
Func(10);
-
Func('A', 20);
-
return 0;
-
}
-
-
using namespace std;
-
-
void Func(int a, char b)
-
{
-
cout << "int a, char b" << endl;
-
}
-
-
void Func(char b, int a)
-
{
-
cout << "char b, int a" << endl;
-
}
-
-
int main(void)
-
{
-
Func(10, 'A');
-
Func('A', 10);
-
return 0;
-
}
2.2不支持函数重载的情况
除了上面讲的三种情况,其他情况都不能构成函数重载
-
-
using namespace std;
-
-
int func(double d)
-
{
-
;
-
}
-
-
void func(double d)
-
{
-
;
-
}
-
-
-
int main()
-
{
-
foo(1.1); // ??? 会不知道这里到底是进 int func 还是 void func
-
return 0;
-
}
-
-
using namespace std;
-
-
void func()
-
{
-
cout << "func()" << endl;
-
}
-
-
void func(int a = 0)
-
{
-
cout << "func(int a)" << endl;
-
}
-
-
int main()
-
{
-
//func(); // 调用存在歧义
-
func(1); // 可以(调用下面的有参数的函数)
-
-
return 0;
-
}
3.引用
3.1引用的概念
-
-
using namespace std;
-
-
int main()
-
{
-
int a = 10;
-
int& ra = a;//<====定义引用类型
-
//(这里取名为 ra,因为引用的英文是 reference,所以后面命名变量时会简写为 r,或者 ref 来代表引用)
-
printf("%p\n", &a);
-
printf("%p\n", &ra);
-
-
cout << a << endl;
-
cout << ra << endl;
-
return 0;
-
}
引用在语法层,我们要理解这里没有开新空间,就是对原来的取了一个新名称而已。
再次注意:
① 引用并不是新定义一个变量,只是给一个变量取别名。
② 编译器不会为引用的变量开辟内存空间,它和它引用的变量会共用同一块内存空间。
3.2引用的特性
-
-
using namespace std;
-
-
int main()
-
{
-
int a = 10;
-
int& ra = a;
-
-
int b = 20;
-
ra = b; // ?
-
-
cout << a << endl;
-
cout << ra << endl;
-
cout << b << endl;
-
cout << ra << endl;
-
-
return 0;
-
}
引用是不会变的,我们定义它的时候它是谁的别名,就是谁的别名了。
以后就不会改了,它是从一而终的!!!
int& ra = a;
3.3引用做参数
我们在C语言教学中讲过 Swap 两数交换的三种方式。
我们当时用的最多的就是利用临时变量去进行交换。
如果把它写成函数形式就是这样:
-
-
using namespace std;
-
-
void Swap(int* px, int* py)
-
{
-
int tmp = *px;
-
*px = *py;
-
*py = tmp;
-
}
-
-
int main()
-
{
-
int a = 10;
-
int b = 20;
-
cout << a << ' ' << b << endl;
-
Swap(&a, &b); // 传址
-
cout << a << ' ' << b << endl;
-
return 0;
-
}
这里我们调用 Swap 函数需要传地址,
因为形参是实参的一份临时拷贝,改变形参并不会对实参产生实质性的影响。
但是,我们学了引用之后我们就可以这么玩:
-
-
using namespace std;
-
-
void Swap(int& ra, int& rb)
-
{
-
int tmp = ra;
-
ra = rb;
-
rb = tmp;
-
}
-
int main()
-
{
-
int a = 10;
-
int b = 20;
-
cout << a << ' ' << b << endl;
-
Swap(a, b); // 这里既没有传值,也没有传地址,而是传引用
-
cout << a << ' ' << b << endl;
-
return 0;
-
}
是怎么做到交换的?
我们知道,形参是定义在栈帧里面的。
实际调用这个函数的时候,才会给 ra 和 rb 开空间。调用这个函数的时候,把实参传给形参。
那什么时候开始定义的?实参传给形参的时候开始定义的。
ra 是 a 的别名,rb 是 b 的别名,所以 ra 和 rb 的交换,就是 a 和 b 的交换。
因此,我们利用这一特点,就可以轻松实现两数的交换。
我们来梳理一下,顺带复习一下之前讲的函数重载。
在我们一共学了三种传参方式:传值、传地址、传引用。
-
-
using namespace std;
-
void Swap(int x, int y)
-
{
-
int tmp = x;
-
x = y;
-
y = tmp;
-
cout << 1 << endl;
-
}
-
-
void Swap(int* px, int* py)
-
{
-
int tmp = *px;
-
*px = *py;
-
*py = tmp;
-
cout << 2 << endl;
-
}
-
-
void Swap(int& rx, int& ry)
-
{
-
int tmp = rx;
-
rx = ry;
-
ry = tmp;
-
cout << 3 << endl;
-
}
-
-
int main()
-
{
-
int a = 10;
-
int b = 20;
-
Swap(&a, &b);
-
//Swap(a, b); // 报错
-
return 0;
-
}
这里 Swap(a,b) 为什么会报错呢?
这三个 Swap 是可以构成函数重载的,
只要不影响它的函数名修饰规则,就不会构影响!
换言之,修饰出来的函数名不一样,就支持重载!
但是 Swap(a,b) 调用时存在歧义。调用不明确!
编译器不知道调用哪一个,是传值还是传引用,所以会报错。
当时再讲数据结构单链表的时候用的是二级指针,当时没有采用头结点的方式。
那么要传指针的地址,自然要用二级指针的方式接收。
现在我们学了引用,我们就可以试着用引用的方法来解决了(这里我们把 .c 改为 .cpp)
-
int a = 10;
-
int& ra = a;
-
-
int* pa = &a;
-
int*& rpa = pa
我们来看如何用引用的方法来实现!
-
-
-
-
-
typedef int SLNodeDataType;
-
-
typedef struct SingleListNode
-
{
-
SLNodeDataType data; // 用来存放节点的数据
-
struct SingleListNode* next; // 指向后继节点的指针
-
} SLNode;
-
-
void SListPrint(SLNode* pHead);
-
void SListPushBack(SLNode*& rpHead, SLNodeDataType x);
-
// ... 略
-
-
-
/* 打印 */
-
void SListPrint(SLNode* pHead)
-
{
-
SLNode* cur = pHead;
-
while (cur != NULL)
-
{
-
printf("%d -> ", cur->data);
-
cur = cur->next;
-
}
-
printf("NULL\n");
-
}
-
-
/* 创建新节点 */
-
SLNode* CreateNewNode(SLNodeDataType x)
-
{
-
//创建,开辟空间
-
SLNode* new_node = (SLNode*)malloc(sizeof(SLNode));
-
//malloc检查
-
if (new_node == NULL)
-
{
-
printf("malloc failed!\n");
-
exit(-1);
-
}
-
//放置
-
new_node->data = x; //存传入的数据
-
new_node->next = NULL; //next默认置空
-
-
return new_node; //递交新节点
-
}
-
-
/* 尾插(指针的引用) */
-
void SListPushBack(SLNode*& rpHead, SLNodeDataType x)
-
{
-
//创建新节点
-
SLNode* new_node = CreateNewNode(x);
-
//如果链表是空的
-
if (rpHead == NULL)
-
{
-
//直接插入即可
-
rpHead = new_node;
-
}
-
else
-
{
-
//找到尾结点
-
SLNode* end = rpHead;
-
while (end->next != NULL)
-
{
-
end = end->next; //令end指向后继节点
-
}
-
//插入
-
end->next = new_node;
-
}
-
}
-
-
-
// 这里我们不传二级指针了。
-
//void TestSList1()
-
//{
-
// SLNode* pList = NULL;
-
// SListPushBack(&pList, 1);
-
// SListPushBack(&pList, 2);
-
// SListPushBack(&pList, 3);
-
// SListPushBack(&pList, 4);
-
//
-
// SListPrint(pList);
-
//}
-
-
// 使用引用的方法:
-
// 我们传 指针的 引用!
-
void TestSList2()
-
{
-
SLNode* pList = NULL;
-
SListPushBack(pList, 1);
-
SListPushBack(pList, 2);
-
SListPushBack(pList, 3);
-
SListPushBack(pList, 4);
-
-
SListPrint(pList);
-
}
-
-
-
int main()
-
{
-
TestSList2();
-
return 0;
-
}
3.4 传值返回
这是我们以前的传值返回:
-
int Add(int a, int b)
-
{
-
int c = a b;
-
return c;
-
}
-
-
int main()
-
{
-
int ret = Add(1, 2);
-
cout << ret << endl;
-
-
return 0;
-
}
这里 return 的时候会生成一个临时变量(c 为 3)
将 3 复制给这个临时变量,然后返回给 ret
如果我们直接把 c 交给 ret,就会出现一些问题。
如果直接取 c 给 ret,取到的是 3 还是 随机值,就要取决于栈帧是否销毁空间!
这个时候严格来说,其实都是非法访问了。
因为这块空间已经还给操作系统了,这就取决于编译器了。
有的编译器会清,有的编译器不会清,这就太玄学了!
所以,在这中间会生成一个临时变量,来递交给 ret 。
而不是直接用 c 作为返回值,造成非法访问。
所以这里不会直接用 c 作为返回值,而是生成一个临时变量。
那么问题来了,这个临时变量是存在哪里的呢?
① 如果 c 比较小(4或8),一般是寄存器来干存储临时变量的活。
② 如果 c 比较大,临时变量就会放在调用 Add 函数的栈帧中。
总结:所有的传值返回都会生成一个拷贝
(这是编译器的机制,就像传值传参会生成一份拷贝一样)
3.5 引用做传值返回
我们已经知道,普通的传值返回会生成一个临时变量了。
我们来试试引用的返回。
这段代码存在的问题:
① 存在非法访问,因为 Add(1, 2) 的返回值是 c 的引用,所以 Add 栈帧销毁后,
会去访问 c 位置空间。
② 如果 Add 函数栈帧销毁,清理空间,那么取 c 值的时候取到的就是随机值,
给 ret 就是随机值,当前这个取决于编译器实现了。VS 下销毁栈帧,是不清空间数据的。
栈帧:C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。
既然不清空间数据,那还担心什么呢?
我们来看看下面这种情况:
-
-
using namespace std;
-
-
int& Add(int a, int b)
-
{
-
int c = a b;
-
return c;
-
}
-
-
int main()
-
{
-
int& ret = Add(1, 2);
-
cout << ret << endl;
-
Add(10, 20);
-
cout << ret << endl; // 这里ret变成30了
-
-
return 0;
-
}
解读:我们并没有动 ret,但是 ret 的结果变成了 30,因为栈帧被改了。
当再次调用 Add 时,这块栈帧的 "所有权" 就不是你的了。
我函数销毁了,栈帧就空出来了,新的函数覆盖了之前那个已经销毁的栈帧,
所以 ret 的结果变成 30 了。
结论就是:不要轻易使用引用返回!
那引用返回有什么存在的意义呢?等我们后面讲完类和对象后再细说。
总结:
日常当中是不建议用引用返回的,如果函数返回时,出了函数的作用域,
如果返回对象还未还给操作系统,则可以使用引用返回,如果已经还给操作系统了,
就不要用引用返回了,老老实实传值返回就行了。
通俗点说就是 —— 看返回对象还在不在栈帧内,在的话就可以使用引用返回。
举个例子:静态变量,全局变量,出了作用域不会销毁
-
int& Count()
-
{
-
static int n = 0;
-
n ;
-
// ...
-
return n;
-
}
注意事项:临时变量具有常性
临时变量是右值(不可被修改),可以读但不能修改。
3.6关于引用的探讨
3.6.1比较传值和传引用的效率
那传值返回和传引用返回的区别是什么呢?
传引用返回速度更快。
以值作为参数或者返回值类型,在传参和返回期间,
函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时拷贝。
因此值作为参数或者返回值类型,效率是非常低下的,
尤其是当参数或者返回值类型(比如一些结构体)非常大时,效率就更低。
传值和传指针在作为传参以及返回值类型上效率相差十分悬殊。
引用的作用主要体现在传参和传返回值:
① 引用传参和传返回值,有些场景下面,可以提高性能(大对象 深拷贝对象)。
② 引用传参和传返回值,输出型参数和输出型返回值。
有些场景下面,形参的改变可以改变实参。
有些场景下面,引用返回,可以减少拷贝、改变返回对象。(了解一下,后面会学)
引用后面用的非常的多!非常重要!
3.6.2 引用和指针的区别
在语法概念上:引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
但是在底层的实现上:实际上是有空间的,因为引用是按照指针方式来实现的。
① 引用是在概念上定义一个变量的别名,而指针是存储一个变量的地址。
② 引用在定义时必须初始化,而指针是最好初始化,不初始化也不会报错。
③ 引用在初始化时引用一个实体后,就不能再引用其他实体,
而指针可以在任何时候指向任何一个同类型的实体。
④ 有空指针,但是没有空引用。
⑤ 在 sizeof 中含义不同,引用结果为引用类型的大小,
但指针始终是地址空间所占字节数(64位平台下占8个字节)
⑥ 引用 即引用的实体增加1,指针 即指针向后偏移一个类型的大小。
⑦ 有多级指针,但是没有多级引用。
⑧ 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
⑨ 引用比指针使用起来相对更加安全。
总结:指针使用起来更复杂一些,更容易出错一些。(指针和引用的区别,面试经常考察)
使用指针有考虑空指针,野指针等等问题,指针太灵活了,所以相对而言没有引用安全!
3.7常引用
如果既要利用引用来提高程序的效率,又想要保护传递给函数的数据不能在函数中被改变,
就应使用常引用。常引用就是在前面引用的语法前 const
语法:const 数据类型& 引用名 = 引用实体;
一共有三种情况:分别是权限的放大、保持权限不变、权限的缩小。
3.7.1 权限的放大
下面是一个引用的例子:
-
int main()
-
{
-
int a = 10;
-
int& ra = a;
-
-
return 0;
-
}
如果对引用实体使用 const 修饰,直接引用会导致报错:
-
int main()
-
{
-
const int a = 10;
-
int& ra = a;
-
-
return 0;
-
}
分析:导致这种问题的原因是,我本身标明了 const,这块空间上的值不能被修改。
我自己都不能修改,你 ra 变成我 a 的引用,意味着你修改 ra 可以修改我的 a,
这就是属于权限的放大问题,a 是可读的,你 ra 要变成可读可写的,当然不行。
那么如何解决这样的问题,我们继续往下看。
3.7.2 保持权限的一致
既然引用实体用了 const 进行修饰,我直接引用的话属于权限的放大,
我们可以给引用前面也加上 const,让他们的权限保持不变。
给引用前面加上 const:
-
int main()
-
{
-
const int a = 10;
-
const int& ra = a;
-
-
return 0;
-
}
解读:const int& ra = a 的意思就是,我变成你的别名,但是我不能修改你。
这样 a 是可读不可写的,ra 也是可读不可写的,这样就保持了权限的不变。
如果我们想使用引用,但是不希望它被修改,我们就可以使用常引用来解决。
3.7.3 权限的缩小
如果引用实体并没有被 const 修饰,是可读可写的,
但是我希望它的引用不能修改它,我们可以用常引用来解决。
a 是可读可写的,但是我限制 ra 是可读单不可写:
-
int main()
-
{
-
int a = 10;
-
const int& ra = a;
-
-
return 0;
-
}
解读:这当然是可以的,这就是权限的缩小。
举个例子,就好比你办身份证,你的本名是可以印在身份证上的,
但是你的绰号可以印在身份证上吗?
所以就需要加以限制,你的绰号可以被人喊,但是不能写在身份证上。
所以,权限的缩小,你可以理解为是一种自我约束
3.7.4 常引用的应用
举个例子:
假设 x 是一个大对象,或者是后面学的深拷贝的对象
那么尽量用引用传参,以减少拷贝。
如果 Func 函数中不需要改变 x,那么这里就尽量使用 const 引用传参。
-
void Func(int& x)
-
{
-
cout << x << endl;
-
}
-
-
int main()
-
{
-
const int a = 10;
-
int b = 20;
-
-
Func(a); // 报错,涉及权限的放大
-
Func(b); // 权限是一致的,没问题
-
-
return 0;
-
}
加 const 后,让权限保持一致:
-
// "加上保持权限的一致"
-
void Func(const int& x)
-
{
-
cout << x << endl;
-
}
-
-
int main()
-
{
-
const int a = 10;
-
int b = 20;
-
-
Func(a); // 权限是一致的
-
Func(b); // 权限的缩小
-
-
return 0;
-
}
解读:如此一来,a 是可读不可写的,传进 Func 函数中也是可读不可写的,
就保持了权限的一致了。b 是可读可写的,刚才形参还没使用 const 修饰之前,
x是可读可写的,但是加上 const 后,属于权限的缩小,x 就是可读但不可写的了。
所以说引用做参数时和以前一样(甚至更建议)函数中不改变参数的值时,在前面 const
常引用后期会用的比较多,现在理解的不深刻也没关系,早晚的事情。
后面讲类和对象的时候会反复讲的,印象会不断加深的。
3.7.5 带常性的变量的引用
先看代码:
-
int main()
-
{
-
double d = 3.14;
-
int i = d;
-
-
cout << d << " " << i << endl;//输出了3.14 3
-
return 0;
-
}
这里的 d 是可以给 i 的,这个在C语言里面叫做 隐式类型转换 。
它会把 d 的整型部分给 i,浮点数部分直接丢掉。
但是我在这里加一个引用呢?
-
int main()
-
{
-
double d = 3.14;
-
int& i = d; // 我能不能用i去引用d呢?
-
-
return 0;
-
}
运行结果:(报错)
直接用 i 去引用 d 是会报错的,思考下是为什么?
这里可能有的朋友要说,d 是浮点型,i 是整型啊,会不会是因为类型不同导致的?
但是奇葩的是 —— 如果我在它前面加上一个 const 修饰,
却又不报错了,这又是为什么?
-
int main()
-
{
-
double d = 3.14;
-
const int& i = d; // ??? 又可以了
-
-
cout << d << " " << i << endl;//输出了3.14 3
-
return 0;
-
}
解析:因为 内置类型产生的临时变量具有常性,不能被修改。
隐式类型转换不是直接发生的,而是现在中间产生一个临时变量。
是先把 d 给了临时变量,然后再把东西交给 i 的:
如果这里用了引用,生成的是临时变量的别名,
又因为临时变量是一个右值,是不可以被修改的,所以导致了报错。
结论:如果引用的是一个带有常性的变量,就要用带 const 的引用。
3.7.6 常引用做参数
前面提到过:使用引用传参,如果函数中不改变参数的值,建议使用 const 引用
举个例子:
一般栈的打印,是不需要改变参数的值的,这里就可以加上 const
void StackPrint(const struct Stack& st) {...}
const 数据类型& 可以接收各种类型的对象。
使用 const 的引用好处有很多,如果传入的是 const 对象,就是权限保持不变;
普通的对象,就是权限的缩小;中间产生临时变量,也可以解决。
因为 const 引用的通吃的,它的价值就在这个地方,如果不加 const 就只能传普通对象。
又到了枯燥的学习知识点的阶段,如果想深入学习的话要学的东西还是多啊。
修炼内功,修炼内功
本篇完。(附下篇链接)
穿越回来贴个下篇链接:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhigkgki
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel图片置于文字下方的方法
PHP中文网 06-27 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22