【C++】入门(二):引用、内联、auto

news2025/2/27 19:26:33

书接上回:【C++】入门(一):命名空间、缺省参数、函数重载

文章目录

  • 六、引用
    • 引用的概念
    • 引用的使用场景
      • 1. 引用做参数
        • 作用1:输出型参数
        • 作用2:对象比较大,减少拷贝,提高效率
      • 2. 引用作为返回值
        • 错误示范
        • 正确示范
      • 3.总结引用的价值
    • 引用特性
    • 引用和指针的区别(面试题)
    • 常引用
  • 七、内联函数
      • 回顾:C语言如何避免函数频繁调用的问题?
      • 面试题1:实现两个数相加的宏函数
      • 面试题2:宏的优缺点
      • 面试题3:C++有哪些技术替换宏?
    • 内联的概念
    • 内联函数的缺点
      • 使用内联:vs:不使用内联
      • 为什么做声明和定义分离?
      • 总结:解决重定义的方案?
  • 八、auto关键字
    • 简介
    • 使用细则
    • auto不能推导的场景
  • 九、基于范围的for循环(C++11)
  • 十、指针空值nullptr(C++11)

六、引用

引用的概念

引用就是给已经存在的变量 取别名。编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

引用的使用场景

1. 引用做参数

作用1:输出型参数

学习C的时候 写Swap()函数 通过传实参地址的方式,使用指针交换两个变量里面的值 。而加入引用这个语法,则通过别名的方式 交换两个变量里面的值。

  void Swap(int* a, int *b)
  {
  	//...
  }
  
  void Swap(int& a, int &b)
  {
  	int tmp = a;
  	a = b;
  	b = tmp;
  }
  
  int main()
  {
  	int x = 0, y = 1;
  	Swap(&x, &y);
  	Swap(x, y);
  
  	return 0;
  }
作用2:对象比较大,减少拷贝,提高效率

对象较大的情况下,对比传值传参 和 传引用传参 的代码效率

#include <time.h>
#include<iostream>
using namespace std;
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void main()
{
	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;
}

我们可以看到一个40000字节的对象,分别使用传值传参、传引用传参,且传参次数10000次。两种传参方式使用的时间有一定差异。引用作为函数参数 消耗的时间小于1 ms,所以是0。
演示结果
总结:这些使用场景,指针也可以适应,但是引用更方便

2. 引用作为返回值

错误示范
  • 一般情况
    1️⃣ 被调函数传值返回。 返回a的拷贝 寄存在寄存器里的临时空间 函数栈帧销毁之前 这块要返回的临时空间就已经生成好了
    在这里插入图片描述
int func()
{
	int a = 0;
	return a;
}
int main()
{
	int ret = func();
	cout << ret << endl;

	return 0;
}
  • 错误使用引用的情况
    2️⃣ 被调函数func传引用返回。 返回a的别名 但函数调用结束,栈帧销毁,a的别名还在访问这个空间——野引用
    在这里插入图片描述
int& func()
{
	int a = 0;
	return a;
}

int main()
{
	int ret = func();
	cout << ret << endl;

	return 0;
}

3️⃣被调函数传引用返回 ,主函数引用接收。返回a的别名、用a的别名接收,意味着ret也是a的别名,但栈帧结束,变量a已经被销毁了,ret还回去访问那块空间,所以ret是野引用。
在这里插入图片描述

int& func()
{
	int a = 0;
	return a;
}

int main()
{
	int& ret = func();
	cout << ret << endl;

	return 0;
}

结论:返回变量出了函数作用域就生命周期就结束了,所以不能用引用返回

正确示范

什么情况下,可以用引用返回?
答:全局变量、静态变量、堆上变量 可以用引用返回

🌰 实际场景中的例子:
使用引用语法修改用C语言写的链表

struct SeqList
{
	int* a;
	int size;
	int capacity;
};

void SLInit(SeqList& sl)
{
	sl.a = (int*)malloc(sizeof(int) * 4);
	// ..
	sl.size = 0;
	sl.capacity = 4;
}

void SLPushBack(SeqList& sl, int x)
{
	//...扩容
	sl.a[sl.size++] = x;
}

// 修改
void SLModity(SeqList& sl, int pos, int x)
{
	assert(pos >= 0);
	assert(pos < sl.size);

	sl.a[pos] = x;
}

int SLGet(SeqList& sl, int pos)
{
	assert(pos >= 0);
	assert(pos < sl.size);

	return sl.a[pos];
}

int main()
{
	SeqList s;
	SLInit(s);
	SLPushBack(s, 1);
	SLPushBack(s, 2);
	SLPushBack(s, 3);
	SLPushBack(s, 4);

	for (int i = 0; i < s.size; i++)
	{
		cout << SLGet(s, i) << " ";
	}
	cout << endl;

	for (int i = 0; i < s.size; i++)
	{
		int val = SLGet(s, i);
		if (val % 2 == 0)
		{
			SLModity(s, i, val * 2);
		}
	}
	cout << endl;

	for (int i = 0; i < s.size; i++)
	{
		cout << SLGet(s, i) << " ";
	}
	cout << endl;
	return 0;
}

演进为C++玩法的版本
C++中结构体中可以定义函数 ➡️ 类 成员变量 、 成员函数

// C++
struct SeqList
{
	// 成员变量
	int* a;
	int size;
	int capacity;

	// 成员函数
	void Init()
	{
		a = (int*)malloc(sizeof(int) * 4);
		// ...
		size = 0;
		capacity = 4;
	}

	void PushBack(int x)
	{
		// ... 扩容
		a[size++] = x;
	}
	// 读写返回变量
	int& Get(int pos)
	{
		assert(pos >= 0);
		assert(pos < size);

		return a[pos];
	}

	int& operator[](int pos)
	{
		assert(pos >= 0);
		assert(pos < size);
return a[pos];
	}
};
int main()
{
	SeqList s;
	s.Init();
	s.PushBack(1);
	s.PushBack(2);
	s.PushBack(3);
	s.PushBack(4);

	for (int i = 0; i < s.size; i++)
	{
		//cout << s.Get(i)<< " ";
		cout << s[i] << " ";
		//cout << s.operator[](i) << " ";
	}
	cout << endl;

	for (int i = 0; i < s.size; i++)
	{
		/*if (s.Get(i) % 2 == 0)
		{
			s.Get(i) *= 2;
		}*/
		if (s[i] % 2 == 0)
		{
			s[i] *= 2;
		}
	}
	cout << endl;

	for (int i = 0; i < s.size; i++)
	{
		cout << s.Get(i) << " ";
	}
	cout << endl;

	return 0;
}

3.总结引用的价值

1️⃣引用做参数
🅰️可以作为输出型参数 。🅱️对象较大,减少拷贝提高效率
2️⃣做返回值
🅰️修改返回对象。 🅱️减少拷贝提高效率

引用特性

  1. 引用必须初始化:定义的引用的时候就要确定该引用是谁的别名

  2. 引用定义后,不能改变指向

  3. 一个变量可以有多个引用 、多个别名

int main()
{
	int a = 0;

	// 1、引用必须初始化
	//int& b;
	// b = c;

	// 2、引用定义后,不能改变指向
	int& b = a;
	int c = 2;
	b = c;  // 不是改变指向,而是赋值

	// 3、一个变量可以有多个引用,多个别名
	int& d = b;

	return 0;
}

思考:引用可以替代指针吗?
答:引用不能完全替代指针,指针和引用的功能是类似的,C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针,引用不能完全替代指针原因:引用定义后,不能改变指向

🌰引用不可以替代指针的栗子:
双向链表中,必须要改变指向,但引用不能改变指向。
在这里插入图片描述
🌰C++的引用 对指针使用比较复杂的场景 可以进行一些替换:
例如:
原来C语言 写链表头插入 实参传头节点指针 的 地址 。形参需要用二级指针接收。

void PushBack(struct Node* phead, int x)
{
	// phead = newnode;
}

void PushBack(struct Node** pphead, int x)
{
	// *pphead = newnode;
}

现在C++ 使用引用: 给指针类型的变量(头节点指针)取别名

void PushBack(struct Node*& phead, int x)
{
	//phead = newnode;
}

int main()
{
	struct Node* plist = NULL;

	return 0;
}

📘杂七杂八的小知识点:数据结构教科书常见代码解读

//给 结构体 重命名为LNode 
//给 结构体类型的指针 重命名为 PNode
typedef struct Node
{
	struct Node* next;
	struct Node* prev;
	int val;
}LNode, *PNode;
//PNode& phead  给PNode变量(结构体类型的指针)取别名 phead 
void PushBack(PNode& phead, int x)
{
	//phead = newnode;
    //给引用赋值相当于改变PushBack()函数外面的链表头结点指针的指向
}

引用和指针的区别(面试题)

从两个维度对比 语法角度、底层角度对比

语法:
1. 引用是别名,不开空间。指针是地址,语法上需要开空间存储地址
2. 引用必须初始化 、指针可以初始化也可以不初始化。
3. 引用不可以改变指向、指针可以改变指向
4. 引用相对更安全 ,没有空引用、但是有空指针,容易出现野指针,不容易出现野引用

底层:
汇编层面上,没有引用,引用都是用指针实现的,引用编译后也转换成指针了

调到反汇编调试观察:

在这里插入图片描述
结论:底层都需要开空间1、引用在底层是用指针实现的。2、语法含义和底层实现是背离的

常引用

由于引用存在一些隐患,所以我们可以加关键字const 使用常引用 。例如下面代码,对于变量b,它是a的别名,如果对引用b修改, 那么a的值就会改变。但是如果不希望a的值被修改,我们就可以给引用前面加上const 使得引用只有读取a的值的权限,但不能修改。例如,a的引用c就是这样的权限。

int main()
{
    int a = 0;
    //eg.1权限正常的引用 
    int &b =a;//b对a可读可写
    b++;
    
    //eg.2权限缩小
    const int& c = a;//c对a只有读取权限
    
    //eg.3权限放大(错误示范)
    const int x = 10;
    //int& y = x;  //注意不能权限放大
    
    //eg.4 权限平移(正确示范)
    const int& y = x //y可以读取x的值
        
    //eg.5 
    const int& z =10; //z是常量的别名
    
    //eg.6
    const int& m = a+x;// a+x表达式的返回值 是一个临时变量 临时变量具有常性
    
    //eg.7 (错误示范)
    int& n = a+x;
    
    return 0;
}

在这里插入图片描述
在这里插入图片描述

七、内联函数

回顾:C语言如何避免函数频繁调用的问题?

➡️宏

面试题1:实现两个数相加的宏函数

#define Add(x,y) ((x)+(y))

易错点:1、宏不是函数 2、不要写分号 3、括号控制优先级

核心点:宏是预处理阶段进行替换

提问:为什么要加里面的括号?
答:因为x、y可能不是被单一变量所替换,而是其他表达式替换x、y ,防止表达式中的个别值先进性加法运算,从而运算符执行顺序和预期不符,导致运算结果错误。

面试题2:宏的优缺点

缺点:
1️⃣坑很多,不易控制 2️⃣ 不能调试 3️⃣没有安全类型的检查
优点:
增强代码复用性、提高效率

面试题3:C++有哪些技术替换宏?

  1. 常量定义 换用 const enum
  2. 短小函数定义 使用内联inline修饰

内联的概念

内联 : 在调用地方展开 。所指的展开就是:不建立栈帧,在当前函数的栈帧里执行要调用函数的代码

反汇编演示 具体是如何不建立栈帧展开的
图片
思考:能不能为所有函数 都加上inline ?
不行 ,➡️ 内联函数的缺点

内联函数的缺点

使用内联🆚不使用内联

假设func()函数100 行代码,并且一共要调用1w次分别考虑使用内联 、不使用内联的情况合计起来有多少指令?
使用内联

假设inline展开,合计指令数目为:100*1w。意味着 最后的可执行程序会变得很大

​解释:100行代码在1W个位置调用,那么1w个位置都会多出100行,所以就是100W行
不使用内联

假设inline不展开,合计指令数目:100+1w

解释:1W个位置调用 底层就会是汇编指令 call func(),call func()后会从符号表找到这个函数的地址 然后去执行该函数的代码。

总结:由此可见,如果是代码量较大的函数 使用inline 内联展开调用的话,会造成代码膨胀。所以内联函数只适合加在代码量较小的函数上!

注意:
1.inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
2.inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就无法找到该函数。

为什么做声明和定义分离?

如果在头文件中进行函数定义,以下代码存在函数名重定义的问题

问题分析
Stack.cpp 包含了头文件 Test.cpp也包含了头文件。头文件的包含 在预处理阶段 会进行内容替换。也就是说Add()函数被定义两次。Stack.cpp Test.cpp 会分别产生Stack.o、Test.o。链接的时候 会把他们的符号表合并在一起,各自都有叫函数名修饰过的Add()函数,编译器就会认为函数重定义。

所以我们要做声明和定义分离!

//Stack.h
#progam once
#include<iostream>
using namespace std
int Add(int a,int b)
{
    cout<<"int Add(int a,int b)"<<endl;
    return a+b;
}
//Stack.cpp
#include"Stack.h"
//Test.cpp
#include"Stack.h"
int main()
{
    Add(1,2);
    return 0;
}

总结:解决重定义的方案?

1.声明和定义分离

2.Static,改变链接属性,该函数地址不会加入符号列表,只在当前文件可见。(适用于大函数)

3.使用内联函数,因为要在调用处展开,所以该函数地址也不会加入符号列表。(适用于小函数)

//Stack.h
#progam once
#include<iostream>
using namespace std
inline int Add(int a,int b)
{
    cout<<"int Add(int a,int b)"<<endl;
    return a+b;
}
//Stack.cpp
#include"Stack.h"
//Test.cpp
#include"Stack.h"
int main()
{
    Add(1,2);
    return 0;
}

八、auto关键字

简介

C++11规定,程序员使用auto关键字就可以不指定数据类型,编译器通过右边的值的类型自动推导出左边值的数据类型。使用auto关键字 必须初始化,因为在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

使用细则

1.auto与指针和引用结合起来使用
auto* 必须初始化为指针 ,auto声明引用类型时则必须加&
🌰栗子

auto p1 = &i
auto* p2 = &i;
//auto* p3 = i;//会报错
auto& r = a;

2.当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

void TestAuto()
{
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

auto不能推导的场景

1、auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a) 
{} 

2、auto不能直接用来声明数组

void TestAuto() 
{ 
int a[] = {1,2,3}; 
auto b[] = {456};
} 

3、为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

4、auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有 lambda表达式等进行配合使用

九、基于范围的for循环(C++11)

依次取数组中值赋值给e,自动迭代,自动判断结束

for (auto e : array)
{
	cout << e << " ";
}
cout << endl;

十、指针空值nullptr(C++11)

C语言中 我们这样初始化指针:

int* p1 = NULL;

但在C++中, NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

 #ifndef NULL
 #ifdef __cplusplus
 #define NULL    0
 #else
 #define NULL    ((void *)0)
 #endif
 #endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:类型匹配的问题

void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	f((int*)NULL);
	return 0;
}

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的 初衷相悖。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器 默认情况下将其看成是一个整形常量,*如果要将其按照指针方式来使用,必须对其进行强转(void )0

注意:

1、在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入 的。 比特就业课

2、在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

3、为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1719053.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无忧易售ERP:解锁TikTok平台订单处理新效能,赋能跨境电商新未来

在这个全球化电商飞速发展的时代&#xff0c;TikTok作为新兴的电商蓝海&#xff0c;正吸引着无数商家的目光。然而&#xff0c;如何在竞争激烈的市场中脱颖而出&#xff0c;高效管理订单&#xff0c;提升顾客体验&#xff0c;成为每个商家必须面对的课题。无忧易售ERP&#xff…

uni-app+php 生成微信二维码 分销海报

主要代码如下&#xff0c;可直接复制调试参数&#xff1a; //查询当前用户是否有分销海报public function user_poster(){$this->checkAuth();//查询会员信息$user $this->getUserInfoById($this->user_id);if(!empty($user[distribution_img])){$result[data] $use…

一文搞懂分布式事务-Saga

Saga定义 Saga模式是一种分布式事务处理模式&#xff0c;用于保证分布式系统中的一系列操作要么全部成功执行&#xff0c;要么全部回滚&#xff0c;以实现一致性的目标。它采用了长事务的概念&#xff0c;将原子操作拆分为多个子事务&#xff0c;并通过补偿机制保证整个事务的…

Linux基本命令的使用(mkdir)

一、Linux必备命令之mkdir • mkdir命令主要用于创建目录 • 语法: mkdir [选项] 目录名称 若指定目录不存在则创建目录&#xff1b; 选项&#xff1a; -p&#xff0c;--parents 需要时创建目录的上层目录&#xff0c;若这些 目录已存在也不当作错误处理 二、Linux必备命令之…

成绩发布小程序哪个好用?

大家好&#xff0c;今天我要来跟大家分享一个超级实用的小秘密——易查分小程序&#xff01;作为老师&#xff0c;你是不是还在为发放成绩而头疼&#xff1f;是不是还在为通知家长而烦恼&#xff1f;别急&#xff0c;易查分小程序来帮你啦&#xff01; 易查分简直是老师们的贴心…

实战经验分享之移动云快速部署Stable Diffusion SDXL 1.0

本文目录 前言产品优势部署环境准备模型安装测试运行 前言 移动云是中国移动面向政府、企业和公众的新型资源服务。 客户以购买服务的方式&#xff0c;通过网络快速获取虚 拟计算机、存储、网络等基础设施服务&#xff1b;软件开发工具、运行环境、数据库等平台服务&#xff1…

活动选择问题(贪心法)

目录 问题概述 实例分析 代码实现 问题概述 实例分析 求解蓄栏保留问题。农场有n头牛,每头牛会有一个特定的时间区间[b,e]在蓄栏里挤牛奶,并且一个蓄栏里任何时刻只能有一头牛挤奶。现在农场主希望知道最少蓄栏能够满足上述要求,并给出每头牛被安排的方案。对于多种可行方案…

【算法】贪心算法简介

贪心算法概述 目录 1.贪心算法概念2.贪心算法特点3.贪心算法学习 1.贪心算法概念 贪心算法是一种 “思想” &#xff0c;即解决问题时从 “局部最优” 从而达到 “全局最优” 的效果。 ①把解决问题的过程分为若干步②解决每一步时候&#xff0c;都选择当前最优解(不关注全局…

如何编写一份高质量的渗透测试报告?

随着网络安全威胁的不断扩展与升级&#xff0c; 渗透测试目前已经成为众多组织主动识别安全漏洞与潜在风险的关键过程。然而&#xff0c;渗透测试的真正价值在于为用户提交一份全面和可操作的渗透测试报告&#xff0c;这份报告不仅仅是一个技术性文档&#xff0c;同时也是促进安…

c语言基础:数组的运用以及在内存中的地址的理解

目录 目录&#xff1a; 1.数组作为函数参数 2.数组在内存中的存储 2.1数组名是什么&#xff1f; 2.2下面我们来探讨二维数组的各个名字表示什么 二维数组的首元素地址是什么呢&#xff1f; *arr表示的是什么呢 &#xff1f;&#xff08;arr是二维数组&#xff09; 1.数组作…

纯分享#126个电商平台集合(包含60个不同国家)值得一看

01 亚洲 中国 淘宝&#xff1a;拥有大量的卖家和商品种类&#xff0c;主要面向中国市场。天猫:淘宝旗下的B2C电商平台&#xff0c;提供品质保证、正品保障的商品。京东:中国第二大B2C电商平台&#xff0c;提供品质保证、正品保障的商品。苏宁易购: 中国家电连锁巨头苏宁旗下的…

玩机进阶教程------修改gpt.bin分区表地址段 完全屏蔽系统更新 fast刷写分区表 操作步骤解析【二】

上期博文简单说明了分区表的基本常识。我们在有些环境中需要屏蔽手机的系统更新选项。除了以前博文中说明的修改系统更新下载文件夹的方法。还可以通过修改分区表类达到目的。在一些辅助维修工具上面带修改分区表功能。修改后效果为屏蔽系统更新和可以恢复出厂。原则上不深刷都…

STP----生成树协议

目的&#xff1a;解决二层环路问题 跨层封装 广播风暴---广播帧在二层环路中形成逆时针和顺时针转动环路&#xff0c;并且无限循环&#xff0c;最终造成设备宕机&#xff0c;网络瘫痪。 MAC地址表的翻摆&#xff08;漂移&#xff09;---同一个数据帧&#xff0c;顺时针接收后记…

pytorch学习笔记2

首先如果遇到conda找不到包&#xff0c;pip老是超时的情况建议添加一下镜像源 conda的 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ cond…

将 cuda kernel 编译成 ptx 和 rocm的hip asm

1&#xff0c;cuda 源码编译 cuda_a_one.cu __global__ void NNNNNVVVVV_one(int *A) {A[333] 777; }编译命令&#xff1a; %.ptx: %.cu nvcc -archsm_70 -ptx $< -o $ 生成的结果&#xff1a; 2, hip 源码编译 hip_a_one.hip__global__ void AAAAAMMMMM_one(int *A…

docker安装Mysql5.7版本

首先Linux系统已经安装好了docker应用。 1.搜索镜像 docker search mysql 2.拉取5.7的镜像 总之,选starts最多的那个就对了。 docker pull mysql:5.7 ~ docker pull mysql:5.7 5.7: Pulling from library/mysql fc7181108d40: Downloading [============> …

基于Spring前后端分离版本的论坛系统-自动化测试

目录 前言 一、测试环境 二、环境部署 三、测试用例 四、执行测试 4.1、公共类设计 创建浏览器驱动对象 测试套件 释放驱动类 4.2、功能测试 注册页面 登录页面 版块 帖子 用户个人中心页 站内信 4.3、界面测试 注册页面 登录页面 版块 帖子 用户个人中心页…

Ubuntu基础使用

快捷键&#xff1a; 命令行打开快捷方式&#xff1a;Ctrl Alt T Ctrl l 清屏 截屏&#xff1a; LinuxOS命令&#xff1a; 显示当前工作目录所有内容&#xff1a;ls &#xff08;-a -l、-lh&#xff09; -a 输入全部内容&#xff08;包括隐藏文件&#xff09; -l 竖向展示…

算法每日一题(python,2024.05.26) day.8

题目来源&#xff08;力扣. - 力扣&#xff08;LeetCode&#xff09;&#xff0c;简单&#xff09; 解题思路&#xff1a; 双指针&#xff0b;交换&#xff0c;使用left和right两个指针&#xff0c;right指针向右移动&#xff0c;left从数组首位开始&#xff0c;当right找到非…

【字典树(前缀树) 哈希映射 后序序列化】1948. 删除系统中的重复文件夹

本文涉及知识点 字典树&#xff08;前缀树) 哈希映射 后序序列化 LeetCode 1948. 删除系统中的重复文件夹 由于一个漏洞&#xff0c;文件系统中存在许多重复文件夹。给你一个二维数组 paths&#xff0c;其中 paths[i] 是一个表示文件系统中第 i 个文件夹的绝对路径的数组。 …