带领你打开C++的神秘之门--完结篇(上)

简介: 带领你打开C++的神秘之门--完结篇

一、函数重载


 函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。


理论看一遍就行啦,还是直接上栗子好理解吧!


🌰栗子


1.1 函数参数类型不同


//函数参数类型不同
#include <iostream>
#include <stdio.h>
//展开部分常用的
using std::cout;
using std::cin;
using std::endl;
namespace cjn
{
  //函数1
  int add(int e1, int e2)
  {
    cout << "整形:";
    return e1 + e2;
  }
  //函数2
  double add(double e1, double e2)
  {
    cout << "浮点形:";
    return e1 + e2;
  }
}
int main()
{
  int a = 0, b = 0;
  double c = 0, d = 0;
  cin >> a >> b;
  //a和b是int型,会调用int add(int e1, int e2)函数
  cout << cjn::add(a, b) << endl;
  cin >> c >> d;
  //c和d是double型,会调用double add(double e1, double e2)
  cout << cjn::add(c, d) << endl;
  return 0;
}


输入:


2 3


3.4 5.2


输出


整形:5


浮点形:8.6


函数1和函数2虽然函数名相同,但是函数的参数不同,构成函数重载.


1.2 函数参数的个数不同


🌰栗子


//函数参数个数不同时
#include <iostream>
#include <stdio.h>
using std::cout;
using std::cin;
using std::endl;
namespace cjn
{
  void fun()        //函数1
  {
    cout << "fun()" << endl;
  }
  void fun(int a)     //函数2
  {
    cout << "fun(int a)" << endl;
  }
}
int main()
{
  cjn::fun();   //会调用函数1
  cjn::fun(0);  //会调用函数2
  return 0;
}


运行结果:


fun()


fun(int a)


此时还有一个特殊情况:


namespace cjn
{
  void fun()        //函数1
  {
    cout << "fun()" << endl;
  }
  void fun(int a=4)     //函数2
  {
    cout << "fun(int a)" << endl;
  }
}
int main()
{
  cjn::fun();//此时编译器不知道应该调用哪一个函数
  return 0;
}


分析:


由于函数2设置了缺省值,所以在不传参时,会产生混乱.


1.3 函数参数顺序不同


🌰栗子


//函数参数顺序不同
#include <iostream>
#include <stdio.h>
using std::cout;
using std::cin;
using std::endl;
namespace cjn
{
  void fun(int a,char b)        //函数1
  {
    cout << "fun(int a,char b)" << endl;
  }
  void fun(char a,int b)        //函数2
  {
    cout << "fun(char a,int b)" << endl;
  }
}
int main()
{
  cjn::fun(1,'c');    //会调用函数1
  cjn::fun('a',0);    //会调用函数2
  return 0;
}


运行结果:


fun(int a,char b)


fun(char a,int b)


1.4 不构成函数重载


函数的返回值不同 :不构成函数重载


//不支持函数重载
namespace cjn
{
  int fun(char a,int b)       //函数1
  {
    cout << "fun(int a,char b)" << endl;
  }
  double fun(char a,int b)        //函数2
  {
    cout << "fun(char a,int b)" << endl;
  }
}


为什么C不支持函数重载,而C++支持?(重点)


上面也说了,返回值不同也不支持函数重载,让我们从底层来揭秘吧!


在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。这些在C语言中的程序环境阶段有细讲.


我们知道,如果我们只有函数的声明,相当于只拿到了承诺,具体的函数定义并没有拿到,要在最后的链接阶段去通过符号表(很重要)拿到函数地址(兑现承诺).


而符号表中对于函数名的修饰规则在C和C++中是不同的.


例如:


//以下列函数为例
int Add(int a, double b)
{
  ......
}
void Swap(double* e1, double* e2)
{
  double tmp = *e1;
  *e1 = *e2;
  *e2 = tmp;
}


由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下图是g++修饰后的名字规则。


C语言和C++的函数名修饰的不同




在C语言中,只是简单的将函数名直接存入符号表,而C++则考虑到函数的参数类型,函数名长度等因素.


而g++环境下的C++中的函数修饰后变成【_Z+函数长度+函数名+类型首字母】


疑问:如果两个函数函数名和参数是一样的,返回值不同构成函数重载吗?


示例:


int add(int a, int b)
{
  ;
}
double add(int a, int b)
{
  ;
}
int main()
{
  add(2, 3);//调用哪个函数?返回值没有体现也无法体现在调用处
  return 0;
}


答案是:不构成函数重载,即使修改了底层函数名修饰规则也不行,因为在调用函数时,返回值无法体现,无法区分该调用哪个函数.


1.5 “extern C”


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


情况1:


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


情况2:


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


在这种混合模式下开发,由于C和C++编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景下,就需要使用extern “C”。在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。


🌰栗子:


为了能在C/C++工程中都能使用,函数声明时需加上extern “C”


#ifdef __cplusplus//如果是c++项目(这里要求用c语言实现)
extern "C"//此时这条语句会执行
{
#endif
int Add(int left, int right);
int Sub(int left, int right);
#ifdef __cplusplus
}
#endif

解释:


#ifdef _cplusplus


}


#endif


作用:


(1):c++工程中保证是以C的方式实现


_如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被extern "C"修饰了,此时C++编译就知道,该函数是按照C的方式编译的,这样在链接时就会按照C的方式找函数名字.


(2):C工程中不受影响


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


二、引用


引入:


C指针玩法:


在C语言阶段,我们可以通过一个指针去找变量,指针就好比是老板的秘书(一级指针),老板太忙了,只给秘书(一级指针)留了联系方式,我们可以通过秘书(一级指针)去找变量,如果秘书(一级指针)也比较忙,就会给秘书也会自己的秘书留下联系方式,即秘书的秘书(二级指针).


C++引用玩法:


例如:有个小女孩真名叫“涂山苏苏”,我们也可以叫她“小蠢货”,”蠢货苏”等别名,或者“苏苏”等其它小名.这些名词虽然不一样,但是代表的内容都是“涂山苏苏”本人,这便是C++引用的做法,取别名.


图解:



示例代码:


# include <iostream>
using std::cout;
using std::cin;
using std::endl;
int main()
{
  int a = 5;
  //下面都是对a的引用,即a的别名.
  int& b = a;
  int& c = a;
  int& d = a;
  int& e = a;
  cout << a << " " << b << " " << c << " " << d << " " << e << endl;
  return 0;
}


运行结果:


5 5 5 5 5


2.1 引用特点:


  1. 引用必须初始化.


int main()
{
  int a = 5;
  //如果我们引用不初始化
  int& b;
  return 0;
}


改代码会显示错误信息:



这也很好理解,引用就类似与取别名,如果连对象都没有,那别名还有啥意义.


  1. 引用一旦确定了引用实体后,就不能像指针一样改变指向了.


int main()
{
  int a = 5;
  int c = 1;
  int& b=a;
  b = c;//这样b就是c的别名了吗?
  cout << a << " " << b << " " << c << endl;
  return 0;
}


运行结果:


1 1 1


b=c并不是将b改为c的别名,而是赋值,将b的值给改了,b改了,那也就等于a也改了.


目录
相关文章
|
1月前
|
Java 编译器 C++
【C++】 | 类和对象完结篇
【C++】 | 类和对象完结篇
|
10月前
|
安全 编译器 程序员
带领你打开C++的神秘之门--完结篇(下)
带领你打开C++的神秘之门--完结篇
62 0
|
10月前
|
存储 安全 编译器
带领你打开C++的神秘之门--完结篇(中)
带领你打开C++的神秘之门--完结篇
71 0
|
10月前
|
编译器 程序员 C语言
带领你打开C++神秘之门--入门篇
带领你打开C++神秘之门--入门篇
38 0
|
11月前
|
Java 编译器 C++
【C++】类和对象(完结篇)(二)
【C++】类和对象(完结篇)
71 0
|
11月前
|
存储 编译器 C++
【C++】类和对象(完结篇)
【C++】类和对象(完结篇)
50 0
|
编译器 C++
<C++>运算符重载完结,详解赋值,关系,函数调用运算符
<C++>运算符重载完结,详解赋值,关系,函数调用运算符
109 0
<C++>运算符重载完结,详解赋值,关系,函数调用运算符
|
6天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
21 0
|
5天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
19 0