C++入门篇(二)

简介: C++入门篇

C++支持函数重载的原因


C语言编译结果(简化)

函数名不同,在符号表中才能区分出来,所以不能支持重载


cf2302396ee90056b9bf4b2da46f8323_a98acb9998a04a97b3f021966467230a.png


C++编译结果(简化)

C++是通过函数修饰规则在符号表中区分的,只要参数不同,修饰的出的函数名就不同,所以支持重载


aaacc00a33410b0af4175ab9ad33540f_ab0fbe97a32c437c988d5b9564d6e9b6.png


重载的概念一直都没有提到返回值,也就是说返回值并不会构成重载原因,但是为什么呢???


int test(int a, int b)
{
  cout << "test(int a,int b)" << endl;
  return a + b;
}
char test(int a, int b)
{
  cout << "test(int a,int b)" << endl;
  return a + b;
}
int main()
{
  test(0,0);
  test(1, 1);
  return 0;
}


在函数调用时存在二义性,没有指定返回值类型,无法进行区分,并不是函数名修饰规则的原因,所以不构成重载


b8daa1e53a3f4b9fef1c280f3d21e4f9_7f0ced13bb934ac8a7d65c9794cb37b0.png


引用


引用概念


引用是给已经存在的变量另取一个名字或者说小名,但是不会为引用变量新开辟内存空间,所以变量和引用变量共用同一块内存空间


变量类型&引用变量名称=引用实体


#include<iostream>
using namespace std;
int main()
{
  int i = 1;
  int& ri = i;
  printf("%p\n", &i);
  printf("%p\n", &ri);
  return 0;
}


dd8d11dc9ce5cff365b18b080f7f476b_7d34c9093cdd446a88d406d8a7b20aed.png


既然是对已存在的变量进行引用,当然数据类型要求是一致的


引用特性


引用在定义时必须要初始化

一个变量可以有多个引用(小名)

若引用(小名)已经引用一个变量,则不能再引用其他变量;即一个小名不能同时称呼两个人


具体应用


形式参数


#include<iostream>
using namespace std;
void Swap(int& ra, int& rb)
{
  int tmp = ra;
  ra = rb;
  rb = tmp;
}
int main()
{
  int a = 0;
  int b = 1;
  printf("%d %d\n", a, b);
  printf("%p %p\n", &a, &b);
  Swap(a, b);
  printf("%d %d\n", a, b);
  printf("%p %p\n", &a, &b);
  return 0;
}


1bb485b1ce3731cf9dc52269342236b4_306bdf6e734742289b82e54b3a7b5071.png

cd83cea8c99a421f29e9e1000c779a7d_e7207fd24c6343c0b3a702ab0ed933a0.png


优点


减少拷贝,提高效率

输出型参数,在函数中修改形参,实参也随之改变

返回值


引用返回

int& Test()
{
  static int i = 0;
  i++;
  return i;
}
int main()
{
  int ret = Test();
  cout << ret << endl;
  return 0;
}


函数栈帧的使用是从上往下(高地址到低地址),当 函数int& Test()结束时,栈帧被销毁。变量i保存在静态区,所以并不会被销毁,然后返回i的别名给 ret


b1c434c3aeff1832f9f69c01e01b6fce_8be6e4ae1b6943beaecef445f456ccf4.png


如果变量i不在静态区


int& Test()
{
  int i = 0;
  cout << &i << endl;
  i++;
  return i;
}
void test()
{
  int a = 0;
  cout << &a << endl;
}
int main()
{
  int& ret = Test();
  cout << ret << endl;
  cout << ret << endl;
  test();
  cout << ret << endl;
  cout << &ret << endl;
  return 0;
}


int& ret = Test()->int&ret = int& i


所以 ret 也就是 i 本身


由于变量i是 int& Test()内的局部变量,所以当函数栈帧销毁时,i的空间也被收回。可以继续访问,但是数据是不确定的,而且 使用cout也会调用函数的


930d7dda988e47eed092280026d927bd_27c35840d0ba4c6589454fa9c8439377.png


当函数 test()开辟栈帧时,碰巧使用前面函数 Test()函数栈帧的空间,也就是说,当访问 ret 时,其实就是访问变量 a 本身


148ec5e75d49bc9c887a6773fab88341_75ac405e79454c629e254f514bb4e01f.png


运行结果也表面,两个函数 Test(), test(),开辟栈帧使用的确实是同一块空间


第一次打印 ret 时,数据没有改变,可能是 ret 是作为参数传递给 cout,所以数值没有改变


第二次打印 ret 时,数据是随机值,也就是 cout调用函数将被收回 i 的空间数据进行了改变


b0cdb1a4c88e53d86d7b115fa80ea803_a0f4f44201aa492781e5e9ffe91c5c86.png


传值返回

int Test()
{
  static int i = 0;
  i++;
  return i;
}
int main()
{
  int ret = Test();
  cout << ret << endl;
  return 0;
}


与引用返回类似,变量 i 存放在静态区,栈帧销毁不会收到影响,反而作为临时变量返回给 ret


52575ac88b906acc5cb57e35d2f67a42_34a6acc816f744e484fb4124f6ae63cd.png


如果变量i不在静态区


int Test()
{
  int i = 0;
  i++;
  return i;
}
int main()
{
  int ret = Test();
  cout << ret << endl;
  return 0;
}


由于变量i是 int& Test()内的局部变量,所以当函数栈帧销毁时,i的空间也被收回。可以继续访问,但是数据是不确定的,而且 使用cout也会调用函数


总结

出了函数作用域,返回变量不存在,不能使用引用返回,引用返回的结果是未定义的(未知的)

出了函数作用域,返回变量还存在,才能使用引用返回


int Test1()
{
  int i = 0;
  i++;
  return i;
}
int Test2()
{
  static int i = 0;
  i++;
  return 0;
}


优点


减少拷贝,提高效率

可修改返回值

变量没有被const修饰时一般是可读可写的,可读可写是权限,在程序中,权限可以缩小,平移,但是不可以扩大


int main()
{
  //a可读可写
  int a = 0;
  //ra是a的别名,同样是可读可写,权限平移
  int& ra = a;
  //rra被const修饰,只能读,权限缩小
  const int& rra = a;
  //b被const修饰,只能读
  const int b = 0;
  //rb是b的别名,可读可写,权限扩大  error
  int& rb = b;
  //rb被const修饰,只能读,权限平移
  const int& rb = b;
  return 0;
}


8242c19b82db391be48ba79abb5bd32a_6fbe2e41932e47f0a733a413394e2cf6.png


结合上面的传引用做参数,一般都是需要const修饰,否则程序会报错


void test(int& x)
{}
int main()
{
  int a = 0;
  const int& rra = a;
  test(a);
  test(rra);
  return 0;
}


21a5481e1d55f9794a095accf7b7a822_517cbb7986774fdc9000fd7fad6ba39a.png

21a5481e1d55f9794a095accf7b7a822_517cbb7986774fdc9000fd7fad6ba39a.png


目录
相关文章
|
1月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
38 2
C++入门12——详解多态1
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
25 3
|
1月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
30 2
|
1月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
80 1
|
1月前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
70 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
20 1
|
1月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
33 1
|
1月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
44 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
1月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
23 0
|
1月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
27 0