首页 > 编程语言 >C++学习

C++学习

时间:2024-09-20 18:48:20浏览次数:10  
标签:函数 int void C++ 学习 重载 pst cout

C++学习第三课 缺省函数、函数重载与引用

C++学习第一课:C++学习须知
C++学习第二课:命名空间域
C++学习第三课:缺省函数与函数重载


文章目录


前言

本篇文章是C++入门的第二篇,延续了上一篇的内容继续谈c++对于c的改进部分:缺省函数、函数重载以及引用的内容

一、C语言的第二个不足:缺省参数(默认参数)的使用

当函数只有一个形参时

void Func(int a = 0)
{
    cout<<a<<endl;
}
int main()
{
    Func();     // 没有传参时,使用参数的默认值
    Func(10);   // 传参时,使用指定的实参
    return 0;
}

当函数有两个及以上的形参时:全缺省函数(上)半缺省函数(下)

void Func(int a = 10, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }
void Func(int a, int b = 10, int c = 20)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

1. 当函数有两个及以上形参时的传参规则

答:从左到右。因此!我们设置半缺省函数(默认函数)的时候应该从右往左设置默认参数
注1:不能指定传参
注2:不能跳着传参

2. 使用场景辨析:函数在定义和申明时是否都需要写缺省值

(1)不支持两个都给
答:因为怕不一致,那就出现听谁的的问题
(2)那是声明给还是定义给呢?
答:声明给

3. 缺省参数的主要用处—以栈为例

初始版:传统静态栈

struct Stack
{
    int* a;
    int top;
    inr capacity;
};
void StackInit(struvt Stack* pst)
{
    pst->a=(int *)malloc(sizeof(int)*4);
    if(pst->a == NULL){
        perror("malloc fail");
        return;
    }    
    pst->top=0;
    pst->capacity=4;
}
int main()
{
    struct Stack st;
    StackInit(&st);
    //插入100个数据
}

坏处:设死了,每次都只开辟一点点,每次都要扩容,即使你知道你要100个,效率太低

升级版:使用传参但不设置缺省参数

struct Stack
{
    int* a;
    int top;
    inr capacity;
};
void StackInit(struvt Stack* pst,int defaultCapacity)
{
    pst->a=(int *)malloc(sizeof(int)* defaultCapacity);
    if(pst->a == NULL){
        perror("malloc fail");
        return;
    }    
    pst->top=0;
    pst->capacity=defaultCapacity;
}
int main()
{
    struct Stack st;
    StackInit(&st,100);
    //插入100个数据
}

好处:如果已知栈需要插入多少数据,那么可以直接传参开辟所需空间,比较灵活

最终版:设置传参且设置缺省值

struct Stack
{
    int* a;
    int top;
    inr capacity;
};
void StackInit(struvt Stack* pst,int defaultCapacity=4)
{
    pst->a=(int *)malloc(sizeof(int)* defaultCapacity);
    if(pst->a == NULL){
        perror("malloc fail");
        return;
    }    
    pst->top=0;
    pst->capacity=defaultCapacity;
}
int main()
{
    struct Stack st1;
    StackInit(&st1,100);
    //插入100个数据

    struct Stack st2;
    StackInit(&st2);
    //不知道要插入几个数据
}

好处:更灵活,即使不知道实际需要多少也有默认值进行开辟,特别是当你需要创建两个栈,一个知道需要多大,另一个不知道的时候,就可以分别处理,格外灵活

C语言解决这种情况的方法:define

struct Stack
{
    int* a;
    int top;
    inr capacity;
};
#define DEFAULT_CAPACITY 100
void StackInit(struvt Stack* pst)
{
    pst->a=(int *)malloc(sizeof(int)* DEFAULT_CAPACITY);
    if(pst->a == NULL){
        perror("malloc fail");
        return;
    }    
    pst->top=0;
    pst->capacity=DEFAULT_CAPACITY;
}
int main()
{
    struct Stack st1;
    StackInit(&st1,100);
    //插入100个数据

    struct Stack st2;
    StackInit(&st2);
    //不知道要插入几个数据
}

分析:其实本质上也是设死了,无法满足不同需求

二、函数重载

粗略理解:函数重载类似于函数的不同词性的一词多义

1. 函数重载的定义

(1)文字定义:函数重载是函数的一种特殊情况,指在同一个作用域 中声明几个功能类似的同名函数,这些函数的形参列表中,或参数个数不同,或参数类型不同,或类型顺序不同,用于解决不同类似数据的类似问题,对于这类函数,使用时C++可以实现自动匹配

注:返回值不同是不构成重载的

构成重载的代码举例:参数类型不同(上)参数个数不同(中)参数类型顺序不同(下)

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;
}
void f()
{
    cout << "f()" << endl;
}
void f(int a)
{
    cout << "f(int a)" << endl;
}
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;
}

不构成重载的典型代码举例

void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}
void f(int b, char a)
{
    cout << "f(int b, char a)" << endl;
}

由于函数重载而产生的歧义代码

void f()
{
    cout << "f()" << endl;
}
void f(int a=10)
{
    cout << "f(int a)" << endl;
}
int main()
{
    f();//无法判断是调用哪个函数
    return 0;
}

注:C语言不允许函数重载的出现

2. cpp是怎么支持函数重载的自动匹配的

注:前面说到的cout的自动识别也是一个道理,或者说也属于函数重载

(1)编译链接过程

  • 预处理:头文件(.h 和 .cpp 文件)展开 + 宏替换 + 条件编译 + 去掉注释…(生成 .i 文件)
  • 编译:检查语法+生产汇编代码(生成 .s 文件)
  • 汇编:汇编代码转换.o为二进制机器码,让CPU能看懂(生成 .o 文件)
  • 链接:把代码、汇编等文件合在一起,形成链接,生成可执行程序(生成 .exe 和 .out 文件)

是不是所有的函数都要去链接找

答:编译阶段有声明并且在后面没有明确的定义被编译才需要,声明了要去链接里面确认

(2)函数名修饰规则
答:前缀+函数名个数+函数名+参数类型首字母 ,如:_Z4funcid
注意:这个只是g++(Linux指令)的修饰规则,其他的并不一样

问题一:为什么cpp可以支持

答:在编译阶段,对于不同重载函数,编译时的函数名修饰规则导致生成的名字不一样,因此指向不同函数

问题二:为什么c不支持

答:因为c语言在编译的函数名修饰时,用的地址是直接函数名,太过直接导致无法区分重载

3. 引用(针对与C指针的优化)

注:首先明确,c++仍然支持C中的各类指针的玩法

(1)引用的概念:引用定义的不是一个新变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量开辟空间,他和他所引用的变量是共用同一块内存空间的

引用代码举例

void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

注1:共用了&这个符号,当用法不同,可以兼顾,注意区分即可
注2:引用是无法更改的,即你定义a是b的别名,a就不可能是别人的别名了
注3:一个变量可以有多个引用
注4:引用在定义时必须初始化

常见错误使用类型

void TestRef()
{
   int a = 10;
   // int& ra;   // 该条语句编译时会出错
   int& ra = a;
   int& rra = a;
   printf("%p %p %p\n", &a, &ra, &rra);  
}
void TestConstRef()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;
    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;
    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;
}

(2)引用的玩法

做参数

void Swap(int& left, int& right)
{
   int temp = left;
   left = right;
   right = temp;
}

做返回值

int& Count()
{
   static int n = 0;
   n++;
   // ...
   return n;
}

应用

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);//ret是c的引用,函数返回后这个内存位置被消耗,ret仍然保存了这个位置的引用,但是可能指向一个被重新分配或者覆盖的内存区域
    Add(3, 4);//这一步返回时,ret访问的还是这个内存,因此结果是7
    cout << "Add(1, 2) is :"<< ret <<endl;//注意:如果你这里多打印几次,结果将不会是7,因为ret是c的引用,和c共用一个内存空间,在add(3,4)结束之后,能指向一个被重新分配或者覆盖的内存区域而导致数值更改
    return 0;
}```

传统代码:使用普通传参/指针

void Swap(int a,int b)
{
    //这种写法输出的a、b和x、y没有直接关系
    int temp = a;
    int a= b;
    int b = temp;
}
int main()
{
    int x = 0;
    int y = 1;
    Swap(x,y);
    return 0;
}

更新代码:使用引用

void Swap(int& a,int& b)
{
    int temp = a;
    int a= b;
    int b = temp;
}
int main()
{
    int x = 0;
    int y = 1;
    Swap(x,y);
    return 0;
}

指针也是变量,因此也可以给指针弄别名(引用)

总结

以上是对缺省函数、函数重载、引用的基本知识,对于函数重载部分,存在着很多底层逻辑的分析以及Linux系统的使用,暂时没办法继续挖掘,时隔十天正式打卡第二篇,再接再厉!

标签:函数,int,void,C++,学习,重载,pst,cout
From: https://blog.csdn.net/m0_74983085/article/details/142067999

相关文章

  • opencascade Bnd_Range源码学习区间计算
    opencascadeBnd_Range前言这个类描述了由两个实数值限定的1D空间中的区间。一个区间可以是无效的,这表示区间中不包含任何点。方法1默认构造函数。创建一个无效区间。Bnd_Range();2构造函数。创建最小最大值区间Bnd_Range(constStandard_RealtheMin,constStandar......
  • 工作之余学习的,内容不多,只是作为一位朝圣者进行学习,望能与诸位多多交流,提高自身产能,提
    usingSystem;namespaceHello_tomorrow{internalclassProgram{staticvoidMain(string[]args){intday=12;inttoday;today=day++;//today=day++--先使用将day赋值给today,在自增;若today=++day--先自增后使用;Console.WriteLine("day={0},today={1}",day,......
  • C++ 多态
    一、多态的概念多态简单来说就是多种形态。多态又分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态(静态多态)主要就是我们一般讲的函数重载和函数模板。运行时多态,具体点就是去完成某个行为(函数),可以传不同的对象就会完成不同的行为,就达到多种形态。就像我们买火......
  • 第三周《密码系统设计》学习总结思维导图
      marmaid代码为:graphLR  A[密码系统设计第三周]-->B[《WindowsC/C++加密解密实战》]  B-->C[第四章]  C-->T[4.2加密基础]  W-->U[CryptoAPI介绍]  T-->V[加密概念]  T-->X[加密类型]  X-->d[对称加密]  X-->......
  • 【Py/Java/C++三种语言OD独家2024E卷真题】20天拿下华为OD笔试之【模拟】2024E-转骰子
    可上欧弟OJ系统练习华子OD、大厂真题绿色聊天软件戳od1441了解算法冲刺训练(备注【CSDN】否则不通过)文章目录相关推荐阅读题目描述与示例题目描述输入描述输出描述示例一输入输出说明示例二输入输出说明解题思路构建长度为6的数组表......
  • C++顺序结构(2)
    一、变量、赋值语句与表达式1、天安门广场在北京市中心,它南北长880米,东西宽500米,试编一程序,计算天安门广场面积是多少平方米。点击查看代码1//试编程,计算天安门广场的面积是多少平方米2#include<iostream>//包含输入输出流头文件iostream3usingnamespacestd;......
  • 八个 C++ 开源项目,帮助初学者进阶成长
    通过参与或阅读开源项目的源代码,可以帮助你深入理解C++的各种概念和技术。ThreadPool一个简单的C++11线程池实现,只有一个头文件,代码加起来不到100行。GitHub地址:https://github.com/progschj/ThreadPoolsudokuC++实现的命令行数独游戏。600余行代码,初学者也可以轻松学习。......
  • 硬件学习指南
        思来想去,还是决定把学习硬件过程记录分享出来,一方面呢给在校大学生提供一些系统的学习思路,另一方面记录下来也方便以后一些资料的查阅。主要内容的话目前想到就从硬件电路设计,PCBlayout和C语言程序设计三个方面入手。从电子元器件的一些常识,到电路设计,程序编写,功......
  • 分块/莫队学习笔记(一)(2024.8.23)
    分块基本概念分块的基本思想是,通过对原数据的适当划分,并在划分后的每一个块上预处理部分信息,从而较一般的暴力算法取得更优的时间复杂度。分块的时间复杂度主要取决于分块的块长,一般可以通过均值不等式求出某个问题下的最优块长,以及相应的时间复杂度。LOJ小分块#6277.数列分......
  • 图论进阶学习笔记(三)(2024.8.12)
    二分图定义如果你能把一个图划分成两个集合,集合内部的点没有边相连接,那么这个图就是一个二分图,如图就是一个二分图:交错路:从一个没有被匹配的点出发,依次走非匹配边,匹配边,非匹配边……最后到达另外一部点当中某个没有被匹配的点的路径。增广路:从一个没有被匹配的点出发,依次走......