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


目录
相关文章
|
5月前
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
84 0
|
9月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
8月前
|
存储 分布式计算 编译器
C++入门基础2
本内容主要讲解C++中的引用、inline函数和nullptr。引用是变量的别名,与原变量共享内存,定义时需初始化且不可更改指向对象,适用于传参和返回值以提高效率;const引用可增强代码灵活性。Inline函数通过展开提高效率,但是否展开由编译器决定,不建议分离声明与定义。Nullptr用于指针赋空,取代C语言中的NULL。最后鼓励持续学习,精进技能,提升竞争力。
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
146 2
C++入门12——详解多态1
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
209 1
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
151 1
|
C语言 C++ 容器
C++入门7——string类的使用-1
C++入门7——string类的使用-1
103 0
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
240 1
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
207 0
C++入门6——模板(泛型编程、函数模板、类模板)