目录
5.2 C++支持函数重载的原理——名字修饰(name Mingling)
❀❀❀没有坚持的努力,本质上并没有多大的意义。
1. C++关键字
#include <iostream> 相当于C语言中的 #include<stdio.h>
2. 命名空间
2.1 命名空间的定义
同一个作用域里面,是不能有同名变量的。
命名冲突,在C语言没有很好的解决这个问题,但是CPP引入了namespace来解决这个问题。
::域作用限定符
命名空间可以定义变量
#include <iostream>
//这是一个命名空间,可以定义变量、函数、类型
namespace ky
{
int a = 0;
int c = 2;
}
int b = 0;
int c = 1;
int main()
{
int b = 1;
int a = 1;
printf("%d\n", b);//b = 1,访问局部变量
printf("%d\n", ::b);//b = 0, 访问全局变量//::域作用限定符
printf("%d\n", a);//a = 1;
printf("%d\n", ky::a);//a = 0;
printf("%d\n", c);//1
printf("%d\n", ::c);//1
printf("%d\n", ky::c);//2
return 0;
}
命名空间可以定义函数、类型
#include <iostream>
namespace yyqx
{
//定义函数
void f()
{
printf("void f()\n");
}
//定义结构
struct ListNode
{
int val;
struct ListNode* next;
};
}
namespace ky
{
struct ListNode
{
int val;
struct ListNode* next;
};
}
int f = 0;
int main()
{
printf("%d\n", f);//0
printf("%p\n", yyqx::f);//结构体的地址
yyqx::ListNode* n1 = NULL;
ky::ListNode* n2 = NULL;
return 0;
}
命名空间可以嵌套而且同名的命名空间是可以同时存在的,编译器编译时会将其合并。
代码1展示:
#include <iostream>
namespace yyqx
{
int a = 1;
namespace A
{
struct Node
{
int val = 0;
struct Node* next= NULL;
};
}
}
namespace yyqx
{
int b = 1;
namespace B
{
struct Node
{
int val = 0;
struct Node* next = NULL;
};
}
}
int main()
{
struct yyqx::A::Node n1;
struct yyqx::B::Node n2;
return 0;
}
命名空间不影响生命周期,是全局变量还是全局变量,只是命名发生冲突时,进行隔离的作用。【命名空间只能放在全局】
2.2 命名空间的使用
(1)加命名空间名称及作用域限定符
(2)使用using将命名空间中某个成员引入
using yyqx::a;//代码1//释放yyqx命名空间里面的变量a //释放的是命名空间就加namespace,如果释放的不是命名空间而是命名空间里的数据,就不用加namespace
(3)使用using namespace 命名空间名称引入
[我们经常使用第二种,既不会容易发生冲突,又不会特别麻烦]
using namespace yyqx;//意思是把yyqx这个命名空间定义的内容释放出来
int main()
{
struct A::Node n1;
struct B::Node n2;
return 0;
}
using namespace yyqx;//意思是把yyqx这个命名空间定义的内容释放出来
using namespace A;//第二层释放
//因为A在yyqx里面,所以上面两行的内容不能进行交换
int main()
{
struct Node n1;
struct B::Node n2;
return 0;
}
这里代码释放A,就不能释放B,否则就发生命名冲突(using相当于没有命名空间)
using namespace yyqx::A;
这个代码相当于释放A,但是并没有释放yyqx;
using namespace std;//std 封C++库的命名空间
3. C++输入&输出
#include <iostream>
using namespace std;
int main()
{
int a = 0;
cin >> a;// >>流提取运算法,相当于scanf
cout << a;// << 流插入运算法
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a = 0;
double b = 0;
cin >> a >> b;// >>流提取运算法,相当于scanf
cout << a << " " << b << endl;// << 流插入运算法 endl相当于换行
cout << a << ":" << b << '\n';
cout << "hello world" << endl;
return 0;
}
(1)endl相当于换行'\n'
(2)<assert.h>在C++中也可以用,但是没有命名空间,<cassert>就有命名空间了
(3)使用 cout 标准输出对象 ( 控制台 )和 cin 标准输入对象 ( 键盘 )时,必须 包含 < iostream > 头文件以及 按命名空间使用方法使用std。(4)C++的输入输出可以自动识别变量类型。(5)旧编译器(vc 6.0)中还支持<iostream.h>格式,<iostream.h>格式没有命名空间
4. 缺省参数
4.1 缺省参数概念
#include <iostream>
using namespace std;
//缺省参数(默认参数)
void Test(int a = 0)
{
cout << a << endl;
}
int main()
{
Test();//没有传参时,使用参数的默认值a = 0
Test(1);//传参时,使用指定的实参 a = 1;
}
4.2 缺省参数分类
(1)全缺省参数(全默认参数)
void Test(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
(2)半缺省参数
void Test(int a , int b = 20, int c = 30)//必须从右向左缺省,并且是连续的
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
1. 半缺省参数必须 从右往左依次来给出,不能间隔着给2. 缺省参数不能在函数声明和定义中同时出现(最好在声明给;声明不给,定义给也不可以)3.缺省值必须是常量或者全局变量4. C语言不支持(编译器不支持)
5. 函数重载
5.1 函数重载概念
(1)参数类型不同
int Add(int left, int right)
{
return left + right;
}
double Add(double left, double right)
{
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;
}
C语言不支持同名函数,CPP支持同名函数。要求:参数名相同,参数不同
代码:
short Add(short left, short right)
{
return left + right;
}
int Add(short left, short right)
{
return left + right;
}
上述代码不是函数重载,因为函数进行调用的时候,并不知道要调用哪一个函数。(仅仅只有返回值不同是不能构造函数重载的)函数重载要求参数不同。而跟返回值没关系。
5.2 C++支持函数重载的原理——名字修饰(name Mingling)
C语言不支持重载,C++支持重载。C++是如何支持的?为什么C语言不支持?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段: 预处理(预编译)、编译、汇编、链接。预处理:头文件展开、宏替换、条件编译、去掉注释
编译:检查语法、生成汇编代码
汇编:把汇编代码转换成二进制的机器码
链接:找调用函数的地址,链接对应上,合并到一起
(1)实际项目通常是由多个头文件和多个源文件构成,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
(2) 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就会到 b.o 的符 号表中找 Add 的地址,然后链接到一起。
//函数1
void f(int a, double b)
{
printf("%d %lf\n", a, b);
}
//函数2
void f(double b, int a)
{
printf("%lf %d\n", b, a);
}
链接的时候,有符号表和函数调用指令。
C语言中,函数1和函数2,生成的符号表,都是f,所以会发生冲突,所以会发生错误,C语言就不支持函数重载
但是在C++中,函数1和函数2生成的符号表是不同的,所以C++就支持函数重载
总而言之还是函数修饰后的名字不同,C语言中仍然是函数名,而C++中是【_Z+函数长度+函数名+类型首字母(如果是指针就多加一个P)】通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
5.3 extern "C"
C语言是可以调用C++和C语言的静态库和动态库的。C++是可以调用C++和C语言的静态库和动态库的。
C++调用C静态库:(虽然C++兼容C,为什么需要用到extern "C"呢?因为函数的命名修饰是不一样的,所以想调用C的函数的时候,符号表找不到我们想要的内容)
extern "C" { #include "../../Stack_C/Stack_C/stack.h" }
然后,右击属性->链接器->常规->附加库目录->找到静态库文件,最后右击属性->链接器->输入->附加依赖项Stack_C.lib;即可
C语言调用C++:(C中没有extern "C",只有C++有)
在C++的文件中,函数前面添加EXTERN_C(这个是随意定义的),然后前面再写一个条件编译(__cplusplus是C++中)[两个杠]
【(第一种写法)条件编译:当在C++中,函数前面经过宏替换就是extern "C",在C语言中,就相当于什么也没有】
(第二种写法)
然后,右击属性->链接器->常规->附加库目录->找到静态库文件,最后右击属性->链接器->输入->附加依赖项Stack_CPP.lib;即可