初识C++之基础认识(2)

本文详细解释了C++中的函数重载机制,包括其在C++中支持的原因,三种不同的重载形式,以及引用的概念、特性及其在编程中的应用场景。同时对比了引用与指针的区别,强调了不同作用域的理解。
摘要由CSDN通过智能技术生成

目录

函数重载

三种函数重载形式

为什么C++支持而c语言不支持? 

linux下查看修饰后的函数名

 引用

概念

 引用特性

常引用

使用场景

 引用与指针

不同的域

提醒


学无止境-尽力学

函数重载

定义:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型 不同的问题。

 其实就是c语言不允许同名函数存在,c++允许同名函数存在但是要求函数名相同参数不同即构成函数重载

三种函数重载形式

参数类型不同

int Add(int left, int right)
{
 cout << "int Add(int left, int right)" << endl;
 return left + right;
}
double Add(double left, double right)
{
 cout << "double Add(double left, double right)" << endl;
 return left + right;
}

 参数个数不同

void f()
{
 cout << "f()" << endl;
}
void f(int a)
{
 cout << "f(int a)" << endl;
}

 参数类型顺序不同-本质还是类型不同

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++支持而c语言不支持? 

 为什么c语言不支持函数重载是因为:c语言在链接的时候是用函数名去查找地址,有同名函数区分不开

 为什么c++可以支持函数重载?

函数名修饰规则(函数修饰规则因不同的编译器而异),名字中引入参数类型,各个编译器自己实现了一套

其实就是只要参数类型不同修饰后的函数名就不同

int add(int a, int b) {
    return a + b;
}
在linux下的g++编译器修饰后,该函数的名称会被修饰为类似于"_Z3addii"这样的字符串,其中"_Z"是一个标志,"3"表示函数名的长度,"add"是函数的名称,"ii"表示两个整型参数。
windows下修饰后的函数名为‘?add@@YAHHH@Z’
需要注意的是,函数修饰规则是C++编译器的内部实现细节,用户通常无需关心

linux下查看修饰后的函数名

c++文件用g++ -o xxx xxx.cpp

c文件用gcc -o xxx xxx.c

最后用objdump -S xxx

 引用

概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间(语法上讲),它和它引用的变量共用同一块内存空间。

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。都是一个人

类型& 引用变量名(对象名) = 引用实体;如果有需要别名可以无限取,随便取。同时能给别名取别名

void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    int*b=&a;
    int*&c=*b;//指针引用
    printf("%p\n", &a);
    printf("%p\n", &ra);
}
需要注意的是,一旦引用被初始化,它将一直引用同一个变量,无法改变引用的目标。因此,一旦 "ra" 被初始化为引用变量 "a",它将一直引用 "a",无法再引用其他变量。

 指针引用

typedef struct Node {
	struct Node* next;
	struct Node* prev;
}LNode,*PNode;
void PushBack(PNode& phead, int a) {
	//
}
int main() {
	PNode list = NULL;
	return 0;
}

 LNode 是一个类型别名,它代表的是一个结构体 Node 的别名。也就是说,LNode 实际上就是 struct Node 的同义词,可以用来声明结构体变量,如 LNode node1;

*PNode 则是一个指针类型的别名,它代表的是一个指向 Node 结构体的指针。在这里*PNode 等价于 struct Node*,可以用来声明指向结构体 Node 的指针变量,如 PNode ptr = &node1;

只要引用了那么ra++==a++,如图: 

 

注意:引用类型必须和引用实体是同种类型

 引用特性

1. 引用在定义时必须初始化-说明是谁的别名

2. 一个变量可以有多个引用

3. 引用一旦引用一个实体,再不能引用其他实体

void TestRef()
{
   int a = 10;
   // int& ra;   // 该条语句编译时会出错
   int& ra = a;
   int& rra = a;
   printf("%p %p %p\n", &a, &ra, &rra);  
}

可以看出a与b地址一样因为b是a引用,而c的地址不同因为是新的变量 

常引用

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;
}

使用场景

1.做参数

可以减少拷贝和提高效率

int* preorderTraversal(struct TreeNode*root,int*returnSize)我们一般是动态开辟malloc

int* preorderTraversal(struct TreeNode*root,int&returnSize)用引用的话减少拷贝

#include<iostream>
using namespace std;
void swap(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
	
}
int main() {
	int n = 1;
	int m = 0;
	swap(n, m);
	cout << n << endl << m << endl;
	return 0;
}
这样方便不用像c语言一样取地址传参
swap(&n, &m);
void swap(int* a, int* b)

2.做返回值

传值返回返回的是返回变量的拷贝-语法规定,也就意味着在销毁前已经将拷贝(临时变量)生成好了即栈帧创好了传过后再销毁,这个过程发生在函数栈帧中。

传引用返回就是返回他的别名

引用做返回值可以修改返回变量

#include <iostream>

int& func(int& x) {
    return x;
}

int main() {
    int a = 10;
    std::cout << "a = " << a << std::endl;

    func(a) = 20;  // 修改返回值所对应的变量
    std::cout << "a = " << a << std::endl;

    return 0;
}
a = 10
a = 20

如果返回变量出了函数作用范围内生命周期到了就要销毁了(局部变量)那么就不能用引用,否则会出现野引用

如果返回变量出了函数作用范围内生命周期没到且没被销毁(静态变量或全局变量)那么就能用引用

#include<iostream>
using namespace std;
int& Count()
{
	static int n = 0;
	n++;
	return n;
}

int main() {
	int& ret = Count();
	cout << ret << endl;
	return 0;
}
最终返回1

 

 引用与指针

引用与指针的功能是类似的,重叠的。

在c++中进行引用是为了在对指针使用比较复杂的情况下进行一些替换,使代码简单易懂,但是引用不能完全代替指针。 

 为什么引用不能完全代替指针?因为引用不能改变指向,例如在链表中增删节点需要改变指向那么引用就不适合了

java与python等其他语言没有指针,他们的链表实现是用了引用,他们的引用可以改变指向。在这些语言中,可以通过改变引用的指向来修改链表的结构。

引用与指针的区别:

语法:

1.引用是别名不需要开辟空间。指针是地址,需要开辟空间存储地址。

2.引用必须初始化,指针可初始化也可以不用初始化

3.引用不能改变指向,指针可以

4.引用相对更安全,没有空引用,不容易出现野引用。指针有空指针容易出现野指针

5.有多级指针,但是没有多级引用

6.sizeof,++,解引用访问有区别

底层:

汇编层面上,没有引用,都是指针,引用编译后变成指针

引用底层是用指针实现的,语法含义和底层实现是背离的

 

不同的域

 在C++中,确实不能在同一作用域内同时使用相同的变量名。这意味着在同一个作用域内不能定义两个同名的变量,包括引用变量

void myFunction() {
    int a = 10;
    int& ra = a;
    
    // ...
    
    int a = 20;  // 错误,重复定义了变量a
    int& ra = a; // 错误,重复定义了引用ra
}

在C++中,有以下几种作用域:

  1. 全局作用域:全局作用域是整个程序中最外层的作用域。在全局作用域中定义的变量可以在程序的任何地方访问。

  2. 类作用域:类作用域是类内部定义的作用域。在类作用域中定义的成员变量和成员函数可以通过类的对象进行访问。

  3. 命名空间作用域:命名空间作用域是命名空间中定义的作用域。命名空间中的变量、函数、类等可以在命名空间范围内被访问。

  4. 局部作用域:局部作用域是在函数、循环或代码块中定义的作用域。在局部作用域中定义的变量只能在该作用域内部访问。

  5. 语句块作用域:语句块作用域是由花括号 {} 包围的代码块中定义的作用域。在语句块作用域中定义的变量只能在该作用域内部访问

自定义的函数在 C++ 中属于局部作用域。当你在函数内定义变量或函数时,这些变量或函数只在该函数内部可见,不能在函数外部被访问或使用。这意味着函数内部定义的变量和函数只在该函数的作用域内有效,称为局部变量和局部函数。当函数执行完毕后,这些局部变量和函数的生命周期也随之结束。

在 C++ 中,函数作用域是局部作用域的一种特殊情况,换句话说,函数作用域就是局部作用域的一部分。

语句块作用域是指由花括号 {} 包围的代码块中定义的作用域。在这样的作用域中定义的变量只在该作用域内部可见,在离开该作用域后就不再有效。这种作用域也称为局部作用域,因为它更为局限,只在特定代码块内起作用

#include <iostream>

int main() {
    int x = 5; // 在main函数的作用域内定义的变量

    {
        int y = 10; // 在语句块作用域内定义的变量
        std::cout << "y 的值为: " << y << std::endl;
    }

    // 在这里无法访问变量 y,因为它是在语句块作用域内定义的
    // std::cout << y; // 这会导致编译错误

    return 0;
}

提醒

提醒:有些知识我们只需要了解一下就好,因为知识很多我们也学不完,没必要强迫自己去学看到的一切除非遇到相关的难点。还有就是语法是语法,底层是底层

也不要说其他语言多么多么好,只能说用途不同,如果真的那么好为什么不只学这一种语言但是有这么多语言

  • 40
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wangsir.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值