C++入门——“为C补坑”

C++入门

C++兼容C语言

入门就是补充一些C语言不支持的(给C语言补坑),为类和对象打基础

#include <iostream>
using namespace std;

int main()
{
	cout << "hello world" << endl;;
	return 0;
}

命名空间

C语言没有解决命名重复的问题

#include <stdlib.h>
 int rand = 0;
//发生命名重复,库里有rand函数,造成命名重复

命名空间域

命名空间的定义

域的概念在C++中非常重要。定义命名空间,需要使用namespace关键字,后面跟命名空间的名字,然后接一对{}即可。{}中即为命名空间的成员。

namespace tl
{
	int rand = 0;
}

C++命名空间可以定义变量、函数、类型。命名空间可以嵌套定义(无限层)。⚠️同一个工程中允许存在多个相同的命名空间,编译器最后会合成同一个命名空间。

同一个域不能定义相同变量,不同的域可以。

一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

命名空间的使用

主要介绍::域作用限定符号

int a = 0;//全局域
int main()
{
	int a = 1;//局部域
	printf("%d\n",a);//就近原则打印a = 1
  printf("%d\n",::a);//空白就是全局域限定
}
命名空间的使用有三种方式
  1. 加入命名空间及作用域限定符
int main()
{
	printf("%d\n", tl::a);
}
  1. 使用using将命名空间中某个成员引入
using tl::b
int main()
{
	printf("%d\n", tl::a);
	printf("%d\n", b);
}
  1. 使用using namespace命名空间名称引入
using namespcae std;

有结构体时,注意符号位置⚠️struct tl::TreeNode node;

访问嵌套的命名空间 struct sql::tl::TreeNdoe node;

要避免出现和库里常见重复的命名,比如rand

加入using namespace tl;后,编译器先在全局域去找,如果还没有就会带展开的tl域中去找。

using namespace std是C++标准库的命名空间;把C++库里的东西暴露出来,展开了就有重复的风险

命名空间使用原则
  1. 项目中,尽量不要using namespace std;
  2. 日常练习用using namespace std;
  3. 项目,指定名空间访问+展开

缺省参数

缺省参数是备胎,在声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

备胎

全缺省参数

void TestFun(int a = 10, int b = 20, int c = 30);

全缺省:函数的每一个参数都有“备胎”

TestFunc(1)这个是给a

TestFunc(1, 2)是给ab

TestFunc(,1,)不可以❌
传参一定是从左到右 想传右边的参数一定要传左边的参数(想传第三个参数必须要传前两个参数)

半缺省参数

void TestFun(int a, int b = 20, int c = 30);

半缺省:函数的部分参数有“备胎”,但是半缺省参数必须从右往左依次来给,不能间隔给。

注意⚠️:

  1. 半缺省参数必须从右往左缺省,不能间隔
  2. 缺省参数不能在函数声明和定义中同时出现。所以只给声明不给定义。
  3. 缺省值必须是常量或者全局变量。
  4. C语言不支持。

函数重载

函数重载概念

C语言不允许同名函数出现,这就很不方便。比如在写两数的加法函数时,需要定义很多不同名函数来满足不同参数类型的需要。
C++支持同名函数,但是函数形参列表(参数个数 或 类型 或 类型顺序)不同。

返回值不同不能用于判定函数重载,因为调用时无法区分

意义

<<流运算符自动识别类型*本质上是函数重载

函数重载的意义:“像用同一个函数一样”

C++支持函数重载的原理——名字修饰(name Mangling)

为什么Cpp支持函数重载,C语言不支持?

首先了解一下编译链接过程:
编译连接的过程:

  1. 预处理:头文件展开、宏替换、条件编译、去掉注释 fun.i,main.i
  2. 编译:语法检查,生成汇编代码 func.s main.s
  3. 汇编:把汇编代码转换为二进制机器码 生成目标文件func.o main.o
  4. 链接:生成可执行程序Linux: a.out win:xxx.exe

链接时,.o的目标文件合并到一起,其次还需要找一些只给声明函数变量地址。比如链接时面对Add函数,链接器会使用哪个名字去找呢?这要看不同编译器的函数名修饰规则。

由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,因此参考使用Linux下的gccg++

gcc的函数修饰后名字不变,而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】

结论:Cpp加入函数名修饰规则(参数不同,修饰出来的名字不同),C语言直接用函数名填入汇编符号表里(函数名没有经过处理)

**就是C++他在链接的时候 符号表里的函数名会根据参数名命名 C只通过函数名命名。**通过这里就理解了C语言没办法支持重载,因为同名函数没有办法区分,而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

C++和C可以互相调用

Cpp的程序能不能调用C的程序,C程序能不能调用Cpp程序?交叉调用

结论:都是可以的,但是需要做一些处理

C++调用C

Cpp调用C:用extern C包含头文件,告诉Cpp的编译器,这里面的这些函数是C的库实现的,你用C的规则去链接查找他们(只能是函数链接,其它不行)

extern "C"
{
	#include "../StackC/Stack.h"
}

C 调用C++

C调用C++ extern _cplusplus条件编译

.c文件里不要用extern "C",因为C编译器不认识extern "C"

在Stack.h里用如下方法修饰:

#ifdef _cplusplus
extern "C"
{
#endif // _cplusplus
		void StackInit(ST* ps);
		void StackDestroy(ST* ps);
		//...
		
#ifdef _cplusplus
}
#endif // _cplusplus

如果是C编译,则不会执行extern "C";C++编译器编译,则会执行extern "C"

引用

引用的概念

引用:大佬觉得指针有时候太复杂了,就搞了一个别名 引用就是给**变量别名**

int& b = a; 引用不开辟新空间,两个共用一个空间

引用的特性

  1. 引用在定义时必须初始化 int& b;否则我都不知道你是哪个别名
  2. 一个变量可以有多个引用,引用也可以引用*;一个变量可以有好几个外号,外号的外号*
  3. 引用一旦引用一个实体,再不能引用其它实体(引用从一而终) ⚠️与指针不同,指针可以改

例题:删除空指针是无害的,不能删除引用。☑️

解答:空指针没有任何指向,删除无害,引用是别名。删除引用就是删除真实对象

常引用

//C++打印变量类型
int a = 10;
cout << typeid(a).name() << endl;
//打印结果为int

const int a = 0; const修饰的变量只可以读不可以写

引用针对const权限平移和缩小,不能放大,对指针也适用

//权限不能放大
const int c = 20;
//int& d = c;❌
const int& d = c;

//权限可以缩小
int e = 30;
const int& f = e;
//强制类型转换或者隐式类型转换会出现临时变量
//临时变量具有常性
int ii = 1;
double dd = ii;
//double& rdd = ii;//临时变量具有常性,权限被放大
const double& rdd = ii;//const只可读

类型转换,并不会改变原变量类型。中间都会产生一个临时变量

函数传参不用引用和指针时,是拷贝,不受权限放大的影响。以下的的传值是可以的。

void Func(int a)
{
  	printf("%d\n", a);
}

int main()
{
  	const int a = 0;
  	Func(a);
  	return 0;
}

建议用const,有非常强的接受度。如果使用引用传参,函数内如果不改变n,那么建议尽量用const引用传参

void func2(const int& n);

C++中,指针和引用用途基本是相似的;指针在C++列表的场景中(链式结构),引用替代不了;其它地方引用基本可以替代指针。

  1. 引用在定义时必须初始化
  2. C++的引用不可以更改指向

指针更强大,更危险,更复杂;引用相对局限一些,更安全,更简单

语法角度而言,引用没有开空间,指针开了4或8个字节。从底层实现的角度而言(需要看懂汇编代码),引用底层是用指针实现的。(拼多多和金鹰买鞋)

引用的场景

1.做参数

a.输出型参数(指针也能做)

b.大对象传参,提高效率

我们现在认为,从语法角度上引用不开空间

2.作返回值

a. 输出型返回对象,调用者可以修改返回对象

b. 减少拷贝,提高效率

如何用引用做返回值是正确的?

传值返回:统一生成一个返回对象拷贝作为函数调用返回值(无论变量在栈还是在静态区储存),不会智能地识别

传引用返回的语法含义:返回返回对象n的别名

总结

  1. 什么情况下不能用引用返回:如果出了函数作用域,返回对象就销毁了。那么一定不能用引用返回,一定要用传值返回。
  2. 出了作用域,对象还在可以用引用返回。

关键看变量出了作用域还在不在

例题:引用传值,指针传地址❌

解答:引用表面好像是传值,其本质也是传地址,只是这个工作由编译器来做,所以错误

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

underratedtang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值