目录:
- 前言
- 关键字
- 一、命名空间
- 1.什么是命名空间
- 2.如何使用命名空间
- 3.如何自己创建命名空间
- 4.为什么要使用命名空间
- 5.命名空间起别名
- 6.匿名命名空间
- 二、缺省参数
- 定义
- 缺省参数类型
- 注意事项
- 三、函数重载
- 定义
- 函数重载的三种方式
- 操作系统的区分方式
- 四、引用
- 定义
- 引用特性
- 使用场景
- 引用和指针的比较
- 总结
前言
打怪升级:第31天 |
---|
C语言是模块化和结构化的语言,适合处理较小规模的程序。对于复杂的问题。需要高度抽象和建模时,C语言则不适用。
为了解决软件危机,我们需要一些可以解决新的问题的语言;
20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上添加了命名空间、缺省参数等特性,并且引入并扩充了面向对象的概念,
通过对C语言知识的不断扩充,内容越来越多,之后便把它独立出来当做一种新的语言,同时为了表明和C语言的渊源,就把它命名为c++。
c++语言之父:Bjarne Stroustrup博士
下面,就让我们一起打开c++的大门, 领略这份跨越40年光阴,经久不衰的语言的魅力。
关键字
首先是对关键字的补充,C语言有32个关键字,c++有63个,几乎是C语言的两倍。
这里我们只做了解。
一、命名空间
#include<iostream>
using namespace std;
int main()
{
return 0;
}
我想上面的代码框架大家都不会陌生,这,就是c++梦开始的地方。
我们先来了解每个语句的含义:
- #include :包含头文件 ;
- using namespace std; :使用标准命名空间std;
- 主函数体。
第一部分的头文件包含与宏定义等这些预处理指令和第三部分的主函数都是C语言同样拥有的,那么这第二句的命名空间又是什么呢,
我想在学校学习的时候大多数老师都是一句“我们在写c++的时候,前面两条语句是必须带上的,大家记住就好。”但是很少有老师会为我们详细介绍,我们只知道如果不写会出错,但是为什么会出错呢?
1.什么是命名空间
故名思路,命名空间是用来存放“名字”的空间,我们可以在命名空间中进行对变量的声明、类的声明、函数的声明和实现、并且还可以嵌套其他的命名空间等。
c++为了和c语言区分开,也为了正确的使用命名空间,规定头文件不写后缀.h;
因此,当我们使用**<iostream.h>的时候,相当于在c中调用库函数,使用的是全局命名空间**;(vs等编译器已经没有这个头文件了)
当我们使用**< iostream >的时候,由于该头文件没有定义全局命名空间,而是把函数声明放到了标准命名空间std**中,所以我们要使用标准函数库中的函数或对象都要使用std来限定。(std是命名空间的标识符)。
2.如何使用命名空间
使用命名空间有三种方法:
- 加命名空间名称及作用域限定符;
- 使用using,将命名空间中成员引入;
- 使用using namespace,将命名空间引入。
下面我们来一一介绍:
1.加命名空间名称及作用域限定符
2.使用using引入命名空间成员
- 使用using namespace 引入命名空间
3.如何自己创建命名空间
创建命名空间需要用到关键字:namespace
语法:namespace 命名空间名 { }
eg:
#include<iostream>
using namespace std;
namespace panda
{
int num = 2023; // 变量
void Print() // 函数
{
cout << "Create namespace panda" << endl;
}
namespace kz // 嵌套命名空间
{
class person{};
}
}
int main()
{
panda::Print();
cout << endl << panda::num << endl;
return 0;
}
4.为什么要使用命名空间
使用命名空间是为了防止命名冲突(名字相同),在C语言中,如果两个变量名字相同那就一定会出错,因为编译器无法对它们进行区分;
但是在c++中,我们可以将它们放到不同的命名空间中,使用不同的变量就在对应的命名空间中寻找即可。
5.命名空间起别名
#include<iostream>
using namespace std;
namespace panda
{
int num = 2023; // 变量
void Print() // 函数
{
cout << "Create namespace panda" << endl;
}
}
namespace p = panda; // 命名空间取别名
int main()
{
panda::Print();
cout << endl << p::num << endl;
return 0;
}
6.匿名命名空间
#include<iostream>
using namespace std;
namespace // 匿名命名空间
{
int num = 2023; // 变量
void Print() // 函数
{
cout << "Create namespace panda" << endl;
}
}
int main()
{
Print();
cout << endl << num << endl;
return 0;
}
运行示例:
使用匿名命名空间时不需要使用命名空间名进行限定,此时如果它外面没有嵌套其他命名空间的话,使用操作就和全局函数类似,
但是如果全局变量中存在同名变量,就需要再次进行区分,
区分方法 -> ::变量名
功能:访问全局区的变量。
eg:
二、缺省参数
定义
缺省参数是指在声明或定义一个函数时为它赋予一个缺省值,调用函数时,若没有传入实参,形参就会使用该缺省值,否则就使用实参。
示例:
#include<iostream>
using namespace std;
void MultiAdd(int a = 0, int b = 20, int c = 30)
{
cout << "a + b + c = " << a + b + c << endl;
}
int main()
{
MultiAdd();
MultiAdd(100);
MultiAdd(1000, 200, 300);
return 0;
}
运行实例:
缺省参数在开辟空间时比较常用,我们既可以提前设置数组大小,也可以使用默认值。
#include<iostream>
using namespace std;
int* GetArr(int size = 10) // 默认大小为10
{
int* tmpp = (int*)malloc(sizeof(int) * size);
if (tmpp == NULL)
{
perror("GetMem::malloc");
exit(1);
}
return tmpp;
}
int main()
{
int* pa = GetArr(); // 使用默认大小
int* pb = GetArr(1000); // 设置开辟数组大小为1000
return 0;
}
缺省参数类型
类型分两种:全缺省和半缺省
举个栗子:
// 全缺省:每个形参都有缺省值
void MultiAdd(int a = 0, int b = 20, int c = 30)
{
cout << "a + b + c = " << a + b + c << endl;
}
// 半缺省:有的形参有缺省值,有的没有,并非必须是一半一半
void MultiAdd(int a, int b = 20, int c = 30) // 注意:缺省值必须从右往左赋予
{
cout << "a + b + c = " << a + b + c << endl;
}
// 都没有缺省值就是普通函数
注意事项
- 缺省参数必须从右往左赋予,不能间隔着给;
- 缺省参数不能在函数定义和声明时同时赋值;
- 分文件编写函数时,只有在头文件中声明函数时可以写上缺省值,不能在定义时给定;
- 缺省值必须是常量或全局变量;
- C语言不支持缺省值;
示例3
总而言之:分文件编写时,缺省值不能在函数定义处赋予。
三、函数重载
在自然语言中,很多词语都不止拥有一个意思,我们可以根据不同的语境需要来进行代入;
eg:学校的电脑坏了,计算机老师一会儿就修理好了。(bushi)
今天张三犯了错,罗老师说一会儿就去修理他。
上面的“修理”这个词语就被重载了。
定义
函数重载:函数的特殊形式,c++允许在同一个作用域下声明多个同名函数,这些函数的参数列表需要有所差异(参数个数、参数类型、参数顺序),常用来处理功能类似,参数类型不同的问题。
举个栗子:
#include<iostream>
using namespace std;
int Add(int a, int b)
{
cout << "Add(int , int);" << endl;
return a + b;
}
double Add(double a, double b)
{
cout << "Add(double , double);" << endl;
return a + b;
}
int main()
{
cout << Add(10, 20) << endl;
cout << Add(10.0, 20.5) << endl;
return 0;
}
函数重载的三种方式
函数重载的前提:同一作用域下,函数名相同的函数。
- 参数个数不同;
- 参数类型不同;
- 参数类型顺序不同。
// 1. 参数个数不同
int Add(int a)
{
return a + 100;
}
int Add(int a, int b)
{
return a + b;
}
// 2.参数类型不同
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
// 3.参数类型顺序不同
double Add(int a, double b)
{
return a + b;
}
double Add(double a, int b)
{
return a + b;
}
操作系统的区分方式
不同编译器下对函数名的修饰各不相同,
由于vs中对函数修饰的规则比较复杂,我们下面在Linux中来查看对函数名的修饰。
Linux对函数名的修饰:
C语言:C语言不支持函数重载,因此不会出现同名函数的问题,所以并不需要再进行修饰。
c++:编译器通过参数列表的不同来区分重载的函数,
规则: _Z + 函数名长度 + 函数名 + 参数列表中参数类型的简写
至于为什么前面是 _z 而不是 _panda ,我想很可能是当时制定命名规则的大佬们没有见过可爱的大熊猫喽~。
示例
C语言:
> c++:
Windows下对函数名的修饰规则,感兴趣的朋友可以看一看。
四、引用
定义
引用不是新定义一个变量,而是给一个已存在的变量起别名,此时编译器不会给引用变量分配内存,而是和它引用的对象共用同一块内存空间。
一个变量可以有一个或多个别名,例如:张三,我们有时也会称之为张某或法外狂徒。语法:类型 & 引用变量名 = 引用实体;
引用特性
- 引用类型必须和引用实体的类型相同;
- 一个变量可以有多个引用;
- 引用必须初始化;
- 引用创建出来后就不能再成为其他实体的引用;
- 常引用。
void TestRef()
{
int a = 10;
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a;
printf("%p %p %p\n", &a, &ra, &rra);
}
常引用:常量的引用必须使用const修饰。
示例:
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; // 该语句编译时会出错,类型不同
double& rd = d;
const int& id = d; //**如果一定要这样写,编译器会给id重新开辟一块空间。**
cout << d << endl << id << endl;
d = 20.05;
cout << d << endl << id << endl;
cout << "-----------------------------------" << endl;
cout << &d << endl << &id << endl;
}
运行实例:
使用场景
- 当参数传递
- 返回值
- 传值和传引用的比较
当参数传递
void Swap(int x, int y)
{
cout << "Swap(int, int);" << endl;
int z = x;
x = y;
y = z;
}
void _Swap(int& x, int& y)
{
cout << "Swap(int&, int&);" << endl;
int z = x;
x = y;
y = z;
}
void test03()
{
int a = 20;
int b = 30;
Swap(a, b);
cout << "a = " << a << ", b = " << b << endl << endl;
_Swap (a, b);
cout << "a = " << a << ", b = " << b << endl;
}
作为返回值
int& Add(int x, int y)
{
int z = x + y;
return z; // 返回的引用不可用
}
void test04()
{
int& cnt = Add(10, 20);
cout << "cnt = " << cnt << endl;
}
传值和传引用的比较
1.做参数
#include <time.h>
struct A { int a[100000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
结果分析:
2.做返回值
结果同上。
引用和指针的比较
在语法上,引用就是一个别名,没有独立的空间,和引用实体共用一块空间。
在底层实现上,实际上是有空间的,因为引用实际上是由指针实现的。
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
引用和指针反汇编代码完全一样。
引用和指针的比较:
- 引用在定义上和引用实体共用一份空间,指针则开辟一份空间存放地址;
- 引用必须初始化,指针则不需要;
- 没有空引用,有空指针;
- 引用在初始化时确定了引用对象后,不能再引用其他变量,指针可以随时指向同类型的变量;
- 不存在多级引用,存在多级指针;
- 可以直接通过引用对象访问引用实体,指针则需要使用解引用(*);
- sizeof(引用对象)的值是引用实体的类型的值,sizeof(指针)的值就是一个地址的大小,与类型无关;
- 引用自加是引用实体的值加1,指针自加是向后偏移一个类型的大小;
- 引用比指针使用起来相对更安全。
总结
以上就是今天讲解的c++中命名空间、缺省参数、函数重载、以及引用的全部内容,如果有什么疑问或者建议都可以在评论区留言,感谢大家对的支持。