C++:C与C++的衔接课(C++基础知识,还请点赞收藏❤)
一、前言
我们都知道,C++是C语言的前身,C++里面的很多语法,都来自于C语言,C++的语法是完全兼容于C语言的。可是,又有一个疑问?有什么问题是非C++不可的呢?
下面,我们将以不破不立的原则,进而铺垫C++的基础知识,并展开讲解。
二、命名空间
1.问题导向
#include #include int rand = 1314; int main() { printf("%d\n", rand); return 0; } /* *这组代码中,就存在了一个不可避免的 会存在编译错误: *error C2365: “rand”: 重定义;以前的定义是“函数” *warning C4477: “printf”: 格式字符串“%d”需要类型“int”的参数,但可变参数 1 *拥有了类型“int (__cdecl *)(void)” */
不难理解,rand是生成随机数的函数名,其函数的声明存放于头文件中,而我们又定义了一个全局变量rand;这就导致了上面的错误,我们称为命名冲突;
- 问:自己写代码的时候,命名稍加注意一下,不就可以了吗?
- 回:虽然你自己写代码的时候,不会发生命名冲突的问题,但你不能保证,多人协作写一个项目的时候,不会出现命名冲突的问题,毕竟每个人的想法都是参差不齐的。
为了解决命名冲突的问题,C++引入了一个新的关键字——namespace,namespace后跟一对{},中间用于变量的定义,函数的声明与定义,类的声明与定义等等;可以为我们在全局域生成一个命名空间域;主要用于解决命名冲突、组织代码、提高代码的可读性和可维护性。
下面的代码,是在域中定义一个malloc为函数名的交换函数,和以rand为变量名的变量。包含头文件后,编译器不会报上面的错误。
2.namespace的定义
- namespace的本质,在用途方面,可以类似于电脑分盘(新建文件夹),在不同的盘符(文件夹)中可以定义相同的文件标识;
- 不同的域可以使用相同的标识符,如局部域和作用域,都可以定义变量a;
- 在同一域中,不能出现两个或两个以上的相同的标识符
- 多文件中,存在相同的命名空间域名的时候,编译的时候,会形成同一个域;
- namespace可嵌套使用;
- namespace只可在全局域中使用;
- C++的作用域主要有四种,函数局部域、全局域、命名空间域、类域。函数局部域、全局域会改变变量或函数的作用域和生命周期,而命名空间域、类域只改变其作用域,不改变其生命周期;
- C++的标准域,定义在 std 域中;
- 编译器找变量、函数、或类的出处,默认是局部域和全局域(先局部域,后全局域);若找不到,则编译器报错;——由此更能体现命名空间对命名冲突问题的解决;
- 下面将对上面所说的某些现象,进行验证与反馈~,若无问题可直接跳转命名空间的使用
//.h文件 #include namespace zmh { void Print(); } //.cpp文件 #include"zmh.h" namespace zmh { void Print() { printf("残风也想永存:第一个C++代码\n"); } } //.main文件 #include"zmh.h" using namespace zmh; // 这里是对命名空间的一种使用方法 int main() { //验证:多文件中,存在相同的命名空间域名的时候,编译的时候,会形成同一个域; //若成功调用Print(),则验证成功!!! Print(); return 0; }
3.命名空间的使用
我们已经知道,编译器找变量、函数、或类的出处,默认是局部域和全局域(先局部域,后全局域);那我们就想,怎么才能告诉编译器,去全局域,去找出处呢?
答案是有的,这里将学习一个新的操作符 ‘::’——作用域解析操作符,而是用于指定某个标识符的所属作用域或命名空间的特殊符号。
‘::’,操作符前是全局域(双冒号前面什么都没有,则代表全局域)或者是命名空间的域名,后面则是我们要访问的变量,函数或类。
命名空间使用总结:
注: ‘::’不仅仅可以访问全局域和命名空间域,但目前们只需要掌握这两个用途即可
如果我们对命名空间域中某一个变量或函数的使用率更频繁的时候,每次都要通过上面的访问方式,是不是有点太过繁琐了?难到就没有其它的解决方法了吗?
是有的,C++为我们提供了一个using关键字,在全局域中使用using关键字,可以解决上面的问题。
其使用方法由以下两种:
using (命名空间域名)::(要访问的变量、函数或类);
// 对某个要频繁使用的变量、函数或类进行展开
using namespace (命名空间域名);// 对整个命名空间域进行展开
功效,可以让我们在使用某个变量、函数或类的时候,不再需要 (命名空间域名)::(要访问的变量、函数或类) ,而是直接(要访问的变量、函数或类)即可。
命名空间使用总结:
命名空间使用方法一:
(命名空间域名)::(要访问的变量、函数或类)
命名空间使用方法二:
using (命名空间域名)::(要访问的变量、函数或类);
// 对某个要频繁使用的变量、函数或类进行展开
命名空间使用方法三:
using namespace (命名空间域名);// 对整个命名空间域进行展开
三、C++的输入&输出流
- :是C++的标准输出、输入流的库函数头文件;其函数的声明与定义在,命名空间std中。
- std::cout:是C++的标准输出函数;可以通过插入运算符 来读取各种类型的数据。
- std::endl:用于在输出流中插入一个换行符,并刷新输出缓冲区;这意味着它不仅仅是插入一个换行符(\n),还会确保所有等待在缓冲区中的输出都被发送到它们的目的地(如屏幕)。
- :流提取符号,用于读取数据。
- C++的cout与cin,会自动识别数据格式,无需,我们再像C语言的printf,scanf一样,要注意每个数据类型对应的占位符。
三、缺省参数
- 定义:所谓缺省参数,是针对函数形参形式来讲的;在函数声明或定义的时候,我们可以直接给形参赋值。当函数调用的时候,如果对应的实参没有被提供,那么将使用这些缺省值;反之则使用被提供的实参。(缺省参数又叫默认参数)
- 函数声明与实现分离的时候,不能重复给缺省参数,且规定只能在函数声明的时候,给缺省参数。
- 缺省参数分为全缺省,或半缺省;全缺省就是函数每个形参都又缺省参数,半缺省就是函数每个形参不全部都是缺省参数。
- C++规定,半缺省参数,只能从右向左给;且函数调用的时候,必须从左向右给值,不能跳跃给值。
- 错误使用案例,还请大家敲入合适的代码,进行测试;下面只展示正确的使用方法,以及函数的行为。
四、重载函数
- 定义:在相同域中,函数名相同,但形参不同的两个或两个以上的函数构成重载函数;
- 重载函数特征1:函数名相同,形参个数不同;
- 重载函数特征2:函数名相同,形参类型不同;
- 重载函数特征3:函数名相同,形参顺序不同;
- 注意事项:不能通过返回值的不同来定义重载函数
- 优势:允许开发者通过相同的函数名,实现形参类型、个数不同,但功能类似的函数;这提高代码的可读性,增加了代码的灵活性;同时函数调用也会显得便利。
// 形参个数不同
void ZMH(int a, double b) { ; }
void ZMH(int a, double b, int c) { ; }
// 形参类型不同void ZMH(int a, double b) { ; }
void ZMH(int a, int b) { ; }
// 形参顺序不同void ZMH(int a, double b) { ; }
void ZMH(double b, int a) { ; }
// 不能通过返回值的不同来定义重载函数
void ZMH(int a, double b) { ; }
int ZMH(int a, double b) { ; }
// 重载函数与缺省参数不恰当的混用,可能存在歧义
void ZMH() { ; }
void ZMH(int a = 4) { ; }
问:上面两组函数,构成重载吗?
答:构成
问:调用ZMH();的时候,是执行哪一个函数呢?
答:编译器不知道执行哪一个函数,会报错。
五、引用
1.引用的意义
讲引用的之前,先插入一个话题:周树人与鲁迅的关系;在革命时期,周树人写的文章很多都是讽刺社会的黑暗,以及国民政党的昏庸,百姓思想的封建。可反动势力,会容忍别人的诋毁吗?当然不会,倘若周树人直接以本名去当文章的作者,说不定上午刚发布完文章,下午反动势力,就闯进家门逮捕了;于是周树人就想着,我能不能取一个别名,去发布文章呢?这样不但降低了被逮捕的风险,而且也能实现自己的目标。
而C++的引入的引用,其价值,与周树人使用“鲁迅”这一笔名来发表文章,在某种意义上是相似的——都是为了避免直接暴露身份或对象,从而达到更安全、更灵活地执行目的的效果。
2.引用的定义
- 引用并非是创建一个新变量,而是引用对象起一个别名,不占用内存空间
- 我们可以通过引用,直接去操作引用对象。
- 引用,可以用在函数的形参,函数的返回值,以及对某一局部变量直接使用引用。
- 一个引用对象,可以有多个引用。
- 引用必须初始化。(指针是推荐初始化,但可以不初始化)
- 引用只能选择一个实体对象。(指针可以随意更改所指向的对象)
3.引用的使用
我们在知道了引用的的重要性和概念之后,我们该如何正确使用引用呢?C++复用了C语言中的取地址操作符——&;在类型之后使用'&'则为引用。 type& 别名 = 引用对象;下面将通过代码例子进行进一步的理解。
4.const 引用
- const type& 别名 = 引用对象。
- const引用可以接受const对象,和普通对象,但普通的引用不能接受const对象——这遵从于权限不能放大,只能缩小。
- 临时对象:程序运行时,会在内存开辟一个临时空间,去存储表达式求值的结果,称为临时对象,且临时对象具有常数性。
- const引用可以接受临时对象,从而达到延长其生命周期的效果。
5.引用的优势
- 引用可以做函数参数和返回值,减少拷贝提高效率;
- 引用传参,可以实现指针传参一样的效果,但指针要通过解引用才能访问到对象;
- 引用,是对引用对象起别名,这使得代码更加的直观,和安全。
六、inline
- 函数声明与定义一起的时候,在其前面加上inline关键字,使得函数在调用的时候,并不会去建立函数栈帧,而是直接在调用函数的内部,将被调用函数的代码展开。这样减少了函数创建栈帧的效率,提高的代码运行效率,此函数称为内联函数。
- 在函数前面加上inline关键字,并非都被编译器所认可;递归函数、有些代码片段较长的函数,不触发内联函数的的机制。一般情况下,函数体小而简单,编译器才会进行内联函数的操作。
- inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。
七、nullptr
- 在C语言中,C++98中,官方将宏NULL,当作空指针的符号,有些情况是void* 类型的0,而有些情况只是0。
- C++中,void* 类型指针,不能隐式转换为其它类型的指针。
- 因此在某些函数重载的时候,NULL是隐形转换为整型0时候,是对于调用重载函数是存在歧义的;而void* 类型的0,编译器会报错。
- 所以C++11引用的nullptr关键字,此关键字总能被转换为任意类型的指针。
- 所以nullptr当作空指针,比NULL更具有安全性。