C++入门(一)(下)

简介: 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该 形参的缺省值,否则使用指定的实参。

4. 缺省参数


4.1 缺省参数概念


    缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该 形参的缺省值,否则使用指定的实参。

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


注: 其中void Func(int a = 0)中的a=0就是缺省函数


4.2 缺省参数分类


(1) 全缺省参数


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


(2) 半缺省参数


#include<iostream>
using namespace std;
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
int main(){
    Func(1);
    return 0;
}


注意:

1.半缺省参数必须从右往左依次来给出,不能间隔着给

2. 缺省参数不能在函数声明和定义中同时出现

3. 缺省值必须是常量或者全局变量

4. C语言不支持(编译器不支持)


//a.h
void Func(int a = 10);
// a.cpp
void Func(int a = 20) {}


// 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那 个缺省值。

5. 函数重载


      自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。 比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前 者是“谁也赢不了!”,后者是“谁也赢不了!”


5.1 函数重载概念


     函数重载:是函数的一种特殊情况,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; }
int main() {
  Add(10, 20); Add(10.1, 20.2); f(); f(10); f(10, 'a'); f('a', 10);
  return 0;
}


5.2 C++支持函数重载的原理--名字修饰(name Mangling)


   为什么C++支持函数重载,而C语言不支持函数重载呢?


   因为在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。


1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道, 【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地 址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?

2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符 号表中找Add的地址,然后链接到一起。

3. 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰 规则。

4. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类 型首字母】。

5..通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

6. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。


(1) 采用C语言编译器编译后结果


503ebaf9f7f9439682a93d8ba837f192.png

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。


(2) 采用C++编译器编译后结果


fb5dc9aa51a841f0a8340eda46fffaba.png


结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息 添加到修改后的名字中。

5.3 extern “C”


由于C和C++编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:


   1. C++中调用C语言实现的静态库或者动态库,反之亦然


   2. 多人协同开发时,有些人擅长用C语言,有些人擅长用C++


       在这种混合模式下开发,由于C和C++编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景 下,就需要使用extern "C"。在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。 下面演示一个在C++工程中使用C语言静态库的例子:


// 创建一个C语言的静态库
//calc.h///
#pragma once
/* * 注意:
* 在实现该库时,并不知道将来使用该静态库的工程是C语言工程还是C++工程
* 为了能在C/C++工程中都能使用,函数声明时需加上extern "C" * * __cplusplus:是C++编译器中定义的宏,即用该宏来检测是C工程还是C++工程
* #ifdef __cplusplus
extern "C"
{
#endif
// 要导出函数的声明
#ifdef __cplusplus
}
#endif

作用:如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被


extern "C"修饰了,


此时C++编译就知道,静态库中的函数是按照C的方式编译的,这样在链接时就会按照C的方式找函 数名字


如果是C工程,编译器未定义_cplusplus宏,编译时该宏无法被是被,则条件编译就无效,函数就 不会被extern "C"修饰

*/

#ifdef __cplusplus
extern "C"
{
#endif
int Add(int left, int right); int Sub(int left, int right);
#ifdef __cplusplus
}
#endif
//calc.c///
#include "calc.h"
int Add(int left, int right) { return left + right; }
int Sub(int left, int right) { return left - right; }


创建一个c++工程,使用上面程序编程工程的静态库

using namespace std;
#include "./../Debug/calc.h" #pragma comment(lib, "./../Debug/CalcLib.lib")
int main() {
  int ret = Add(10, 20);
  cout << ret << endl;
  ret = Sub(30, 20);
  cout << ret << endl;
  return 0;
}


如果在实现静态库时,函数没有使用extern "C"修饰:就会报无法找到xxx函数的链接错误。


1>TestCalc.cpp 1>TestCalc.obj : error LNK2019: 无法解析的外部符号 "int __cdecl Add(int,int)" (? Add@@YAHHH@Z),函数 _main 中引用了该符号
1>TestCalc.obj : error LNK2019: 无法解析的外部符号 "int __cdecl Sub(int,int)" (? Sub@@YAHHH@Z),函数 _main 中引用了该符号
1>D:\WorkStations\Gitee\cppLesson\TestCalc\Debug\TestCalc.exe : fatal error LNK1120: 2


个无法解析的外部命令


函数重载面试题浅析


函数重载的规则:


1.函数名称必须相同。


2.参数列表必须不同(个数不同或类型不同或参数排列顺序不同)。

3.函数的返回类型可以相同也可以不相同。

4.仅仅返回类型不同不足以成为函数的重载。


多态:用同一个东西表示不同的形态;


多态分为:

静态多态(编译时的多态);

动态多态(运行时的多态);

函数重载是一种静态多态;


1、C语言中为什么不能支持函数重载?


       C语言只是在函数名前添加了下划线,因此当工程中存在相同函数名的函数,就会产生冲突。


2、C++中函数重载底层是怎么处理的?


     编译器在底层实际使用的不是函数的名字,而是被重新修饰过的一个较复杂的名字,被重新修饰后的名字包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在最终的名字中,就可保证名字在底层的全局唯一性。


3、C++中能否将一个函数按照C的风格来进行编译?


     有时候C++工程中可能需要将某些函数按照C的风格来进行编译,在函数前加 extern “C” ,意思是告诉编译器,将该函数按照C语言的规则编译。


注意:

重载函数可以出现默认参数,但要注意二义性问题:    

void foo(); void foo(int a=0);

重载函数中编译时根据参数表进行选择

构造函数重载将会给初始化带来多种方式

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