C++心决之命名空间、重载函数和引用
一枕眠秋雨>o< 2024-07-22 11:05:01 阅读 74
目录
1. C++关键字(C++98)
2. 命名空间
2.1 命名空间定义
2.2 命名空间使用
3. C++输入&输出
4. 缺省参数
4.1 缺省参数概念
4.2 缺省参数分类
5. 函数重载
5.1 函数重载概念
5.2 C++支持函数重载的原理--名字修饰(name Mangling)
6. 引用
6.1 引用概念
6.2 引用特性
6.3 常引用
6.4 使用场景
6.5 传值、传引用效率比较
6.6 引用和指针的区别
1. C++关键字(C++98)
C++
总计
63
个关键字,
C
语言
32
个关键字
2. 命名空间
在
C/C++
中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存
在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是
对标识符的名称进行本地化
,
以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的
2.1 命名空间定义
定义命名空间,需要使用到
namespace
关键字
,后面跟
命名空间的名字
,然
后接一对
{}
即可,
{}
中即为命名空间的成员
一个命名空间就定义了一个新的作用域
,命名空间中的所有内容都局限于该命名空间中
2.2 命名空间使用
命名空间中成员该如何使用呢?比如:
<code>namespace bit
{
// 命名空间中可以定义变量/函数/类型
int a = 0;
int b = 1;
int Add(int left, int right)
{
return left + right;
比特就业课
命名空间的使用有三种方式:
加命名空间名称及作用域限定符
使用using将命名空间中某个成员引入
使用using namespace 命名空间名称 引入
3. C++输入&输出
新生婴儿会以自己独特的方式向这个崭新的世界打招呼,C++刚出来后,也算是一个新事物,
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 编译报错:error C2065: “a”: 未声明的标识符
printf("%d\n", a);
return 0;
}
命名空间的使用有三种方式:
加命名空间名称及作用域限定符
int main()
{
printf("%d\n", N::a);
return 0;
}
使用
using
将命名空间中某个成员引入
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
使用
using namespace
命名空间名称 引入
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
3. C++输入&输出
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}
1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件 以及按命名空间使用方法使用std。 2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。 3. <<是流插入运算符,>>是流提取运算符。 4. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。 C++的输入输出可以自动识别变量类型。 5. 实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识, 这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有 一个章节更深入的学习IO流用法及原理。
注意:早期标准库将所有功能在全局域中实现,声明在
.h
后缀的头文件中,使用时只需包含对应
头文件即可,后来将其实现在
std
命名空间下,为了和
C
头文件区分,也为了正确使用命名空间,
规定
C++
头文件不带
.h
;旧编译器
(vc 6.0)
中还支持
<iostream.h>
格式,后续编译器已不支持
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
// 可以自动识别变量的类型
cin>>a;
cin>>b>>c;
cout<<a<<endl;
cout<<b<<" "<<c<<endl;
return 0;
}
std
命名空间的使用惯例:
std
是
C++
标准库的命名空间,如何展开
std
使用更合理呢?
1.
在日常练习中,建议直接
using namespace std
即可,这样就很方便。
2. using namespace std
展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型
/
对
象
/
函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模
大,就很容易出现。所以建议在项目开发中使用,像
std::cout
这样使用时指定命名空间
+
using std::cout
展开常用的库对象
/
类型等方式。
4. 缺省参数
4.1 缺省参数概念
缺省参数是
声明或定义函数时
为函数的
参数指定一个缺省值
。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(10); // 传参时,使用指定的实参
return 0;
}
4.2 缺省参数分类
全缺省参数
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.
半缺省参数必须
从右往左依次
来给出,不能间隔着给
2.
缺省参数不能在函数声明和定义中同时出现
3.
缺省值必须是常量或者全局变量
4. C
语言不支持(编译器不支持)
5. 函数重载
5.1 函数重载概念
函数重载:
是函数的一种特殊情况,
C++
允许在
同一作用域中
声明几个功能类似
的同名函数
,这
些同名函数的
形参列表
(
参数个数 或 类型 或 类型顺序
)
不同
,常用来处理实现功能类似数据类型
不同的问题。
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.
由于
Windows
下
vs
的修饰规则过于复杂,而
Linux
下
g++
的修饰规则简单易懂,下面我们使
用了
g++
演示了这个修饰后的名字。
5.
通过下面我们可以看出
gcc
的函数修饰后名字不变。而
g++
的函数修饰后变成【
_Z+函数长度+
函数名
+
类型首字母】
采用
C++
编译器编译后结果
在
linux
下,采用
g++
编译完成后,函数名字的修饰发生改变,编译器将函数参
数类型信息添加到修改后的名字中。
6.
通过这里就理解了
C
语言没办法支持重载,因为同名函数没办法区分。而
C++
是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载
。
7.
如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办
法区分。
6. 引用
6.1 引用概念
类型
&
引用变量名
(
对象名
) =
引用实体;
<code>void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
引用类型
必须和引用
实体
是
同种类型
的
6.2 引用特性
1.
引用在
定义时必须初始化
2.
一个变量可以有多个引用
3.
引用一旦引用一个实体,再不能引用其他实体
6.3 常引用
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;
}
6.4 使用场景
1.
做参数
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
2.
做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
如果函数返回时,出了函数作用域,如果返回对象还在
(
还没还给系统
)
,则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回。
6.5 传值、传引用效率比较
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
传值和指针在作为传参以及返回值类型上效率相差很大
6.6 引用和指针的区别
在
语法概念上
引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
在
底层实现上
实际是有空间的,因为
引用是按照指针方式来实现
的
引用和指针的不同点
:
1.
引用概念上定义一个变量的别名,指针存储一个变量地址。
2.
引用
在定义时
必须初始化
,指针没有要求
3.
引用
在初始化时引用一个实体后,就
不能再引用其他实体
,而指针可以在任何时候指向任何
一个同类型实体
4.
没有
NULL
引用
,但有
NULL
指针
5.
在
sizeof
中含义不同
:
引用
结果为
引用类型的大小
,但
指针
始终是
地址空间所占字节个数
(32
位平台下占
4
个字节
)
6.
引用自加即引用的实体增加
1
,指针自加即指针向后偏移一个类型的大小
7.
有多级指针,但是没有多级引用
8.
访问实体方式不同,
指针需要显式解引用,引用编译器自己处理
9.
引用比指针使用起来相对更安全
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。