C++11详解(一) -- 列表初始化,右值引用和移动语义

文章目录

  • 1.列表初始化
    • 1.1 C++98传统的{}
    • 1.2 C++11中的{}
    • 1.3 C++11中的std::initializer_list
  • 2.右值引用和移动语义
    • 2.1左值和右值
    • 2.2左值引用和右值引用
    • 2.3 引用延长生命周期
    • 2.4左值和右值的参数匹配问题
    • 2.5右值引用和移动语义的使用场景
      • 2.5.1左值引用主要使用场景
      • 2.5.2移动构造和移动赋值
      • 2.5.3右值引用和移动语义解决传值返回问题
        • 2.5.3.1右值对象构造,只有拷贝构造,没有移动构造的场景->拷贝构造
        • 2.5.3.2右值对象构造,有拷贝构造,也有移动构造的场景->移动构造
        • 2.5.3.3右值对象赋值,只有拷贝构造和拷贝赋值,没有移动构造和移动赋值的场景->拷贝构造和拷贝赋值
        • 2.5.3.4右值对象赋值,既有拷贝构造和拷贝赋值,也有移动构造和移动赋值的场景->移动构造和移动赋值
        • 2.5.3.5 移动构造和移动赋值的意义
  • 3.面试经常问到的

1.列表初始化

1.1 C++98传统的{}

C++98的{}主要支持数组结构体的初始化

struct Hello
{
	int _a;
	int _b;
};

int main()
{
	int a[] = { 1,2,3,4,5 };
	int b[5] = { 0 };

	Hello c = { 1,2 };

	return 0;
}

1.2 C++11中的{}

  1. C++11规定了一切对象都可以用{}初始化,{}初始化也叫列表初始化
  2. 内置类型可以用{}初始化
  3. C++98支持单参数的类型转换,也可以不用{}
Date d3 = { 2025 };// C++11
Date d4 = 2025;// C++98
string s = "11111";
// 单参数的支持隐式类型转换
// 不支持,只有{}初始化才能省略=
// Date d7 2025;

vector<Date> v;
v.push_back(d5);// 有名对象
v.push_back(Date( 2025,1,2 ));// 匿名对象
v.push_back({ 2025,1,1 });// {}

map<string, string> dict; 
dict.insert({ "string","字符串" });
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "Date(const Date& d)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	// 1. 内置类型
	int x1 = { 2 };

	// 2.自定义类型,构造,隐式类型转换,构造 + 拷贝构造
	// 优化为了直接构造
	Date d1 = { 2025,1,12 };
	// 直接调用构造
    Date d10(2025,1,1);
    
	// d2引用的是{2025,1,15}构造的临时对象
	const Date& d2 = {2025,1,15};

    // 可以省略掉=
    Hello p{ 1,2 };
    int a{ 2 };
    Date d5{ 2025,1,1 };
    const Date& d6{ 2025,1,2 };
    
	return 0;
}

1.3 C++11中的std::initializer_list

  1. ⼀个vector对象,我想⽤N个值去构造初始化,那么我们得实现很多个构造函数才能⽀持,因为参数的个数不同
    vector v1 ={1,2,3};
    vector v2 = {1,2,3,4,5};
  2. C++11库中提出了⼀个std::initializer_list的类底层是一个数组,将数据拷贝过来,std::initializer_list中有两个指针,分别指向数组的开始和结束
  3. std::initializer_list支持迭代器遍历
  4. 有了std::initializer_list就可以进行多个值的初始化,比如{x1,x2,x3…}
vector<int> v1 = {1,2,3,4};
vector<int> v2 = {10,20,30,1,2,3,4};
const vector<int>& v3 = {10,20,30,1,2,3,4};

// {}初始化可以省略=
vector<int> v1{1,2,3,4};
vector<int> v2{10,20,30,1,2,3,4};
const vector<int>& v3{10,20,30,1,2,3,4};

// initializer_list 构造
vector<int> v4({1,2,3,4,5,6});

// a数组的指针和il1的两个指针都在栈上,数组也在栈上
// a数组和这两个指针的位置非常接近
initializer_list<int> il1 = {1,2,3};
int a[] = {1,2,3};
  1. map的initializer_list的初始化
    外层是initializer_list,内层是pair的键值对的初始化
// initializer_list + {}pair初始化的隐式类型转换
map<string, string> dict = { {"sort","kai"},{"string","men"} };

2.右值引用和移动语义

1. C++98中的引用是左值引用,C++11中有了右值引用

type & x 左值引用
type && y 右值引用

2.1左值和右值

1.左值是一个表示数据的表达式(如变量名解引用的指针),一般是有持久状态,存储在内存中,我们可以获取它的地址,左值可以出现赋值符号的左边,也可以出现在赋值符号右边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址
2.右值也是⼀个表示数据的表达式,要么是字面值常量、要么是表达式求值过程中创建的临时对象等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址
3.区分左值和右值的重要特点就是是否可以取地址

int main()
{
	// 1.左值 p,b,c,*p,s,s[0]
	// 变量
	int* p = new int(0);
	int b = 1;
	const int c = b;

	// 解引用
	*p = 10;

	// 有名对象
	string s("111111");

	// 函数的返回类型是引用
	s[0] = 'x';

	// 2.右值 10 x + y fmin(x,y) string("111")
	// 右值通常在寄存器中或者#define直接替换了
	// 所以不能够直接取地址
	int x = 0, y = 0;

	// 字面量常量
	// 10
	
	// 表达式的返回值
	x + y;

	// 函数的返回值是存在寄存器中的或是临时对象的拷贝
	fmin(x, y);

	// 匿名对象
	string("111");

	return 0;
}

2.2左值引用和右值引用

1. Type& r1 = x; Type&& rr1 = y;
第一个语句就是左值引用,左值引用就是给左值取别名;第二个就是右值引用,右值引用就是给右值取别名

左值引用取别名
int*& r1 = p;
int& r2 = b;
int& r6 = *p;
const int& r3 = c;
string& r4 = s;
char& r5 = s[0];

右值引用取别名
int&& p1 = 10;
int&& p2 = x + y;
int&& p3 = fmin(x, y);
string&& p4 = string("111");

2. 左值引用不能直接引用右值,但是const左值引用可以引用右值,因为右值通常具有常性

对上面的解释
1.右值到左值要权限放大(但是权限不能放大)
2.权限可以平移

// 左值引⽤不能直接引⽤右值,但是const左值引⽤可以引⽤右值
const int& rx1 = 10;
const double& rx2 = x + y;
const double& rx3 = fmin(x, y);
const string& rx4 = string("11111");

3.右值引用不能直接引用左值,但是右值引用可以引用move(左值)

// 右值引⽤不能直接引⽤左值,但是右值引⽤可以引⽤move(左值)
int&& rrx1 = move(b);
int*&& rrx2 = move(p);
int&& rrx3 = move(*p);
string&& rrx4 = move(s);
// move的底层其实是强制类型转换
// 把左值强转为右值(s有名对象为左值)
string&& rrx5 = (string&&)s;

move实际是一个函数模版,其实也涉及到了引用折叠,后面会细讲
在这里插入图片描述

// (左值)强转之后不会改变本身的属性
// 强转之后再使用左值,还是左值本身的属性
// 用的只是临时对象
// b、r1、rr1都是变量表达式,都是左值
cout << &b << endl;
cout << &r1 << endl;
cout << &rr1 << endl;

int i = 1;
int* pi = (int*)i;
i还是int类型不会改变类型

4. 需要注意的是变量表达式都是左值属性,也就意味着一个右值被右值引用绑定后,右值引用变量变量表达式的属性是左值

左值引用的属性是左值
右值引用的属性也是左值
int&& rr1 = 10;
引用完rr1的属性变为左值
rr1的属性是左值,所以不能被右值引用绑定,除非move一下
int& rr6 = rr1;// 不报错
int&& rrx6 = rr1;// 报错
int&& rrx6 = move(rr1);

5. 在语法层面,左值引用和右值引用都是取别名,不开空间;在底层都是指针实现的

2.3 引用延长生命周期

1. 可以延长临时对象和匿名对象的生命周期,临时对象和匿名对象的生命周期只在当前的一行

std::string s1 = "Test";
const左值引用延长生命周期
const std::string& r1 = s1 + s1;
右值引用延长生命周期
std::string&& r2 = s1 + s1;
延长到r1和r2使用完

2.4左值和右值的参数匹配问题

1.C++98中,我们实现⼀个const左值引用作为参数的函数,那么实参传递左值和右值都可以匹配。

template<class T>
void func(const T& x)
{}
传左值,权限缩小,左值传左值
传右值,权限平移,右值传const左值

2. C++11以后,分别重载左值引用、const左值引用、右值引用作为形参的f函数,那么实参是左值会
匹配f(左值引用),实参是const左值会匹配f(const 左值引用),实参是右值会匹配f(右值引用),也就是编译器会调用最匹配的类型,如果没有右值引用,会调用const左值引用

void f(int& x)
{
	std::cout << "左值引用重载 f(" << x << ")\n";
}

void f(const int& x)
{
	std::cout << "到 const 的左值引用重载 f(" << x << ")\n";
}

void f(int&& x)
{
	std::cout << "右值引用重载 f(" << x << ")\n";
}

int main()
{
	int i = 1;
	const int ci = 2;
	f(i); // 调用 f(int&)
	f(ci); // 调用 f(const int&)
	f(3); // 调用 f(int&&),如果没有 f(int&&) 重载则会调用 f(const int&)
	f(std::move(i)); // 调用 f(int&&)
    
	return 0;
}

3. 右值引用变量在用于表达式时属性是左值

    右值引用本身的属性是左值
    int&& x = 1;
    f(x);调用f(int& x)
    f(std::move(x));调用f(int&& x)

2.5右值引用和移动语义的使用场景

2.5.1左值引用主要使用场景

1.左值引用的主要场景是在函数中,左值引用传参左值引用传返回值减少拷贝,同时左值引用传参在函数中修改形参可以改变实参,传引用返回可以修改返回对象
2.左值引用已经解决大多数场景的拷贝效率问题,但是有些场景不能使用传左值引用返回,如addStrings和generate函数里面返回的是局部对象,C++98中的解决方案只能是被迫使用输出型参数解决(传值返回)。
3.那么C++11以后这里可以使用右值引用做返回值解决吗?显然是不可能的,因为这里的本质是返回对象是一个局部对象,函数结束这个对象就析构销毁了,右值引用返回也无法概念对象已经析构销毁的事实。

class Solution 
{
public:
	// 传值返回需要拷⻉
	string addStrings(string num1, string num2) 
	{
		string str;
		int end1 = num1.size() - 1, end2 = num2.size() - 1;
		// 进位
		int next = 0;
		while (end1 >= 0 || end2 >= 0)
		{
			int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
			int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
			int ret = val1 + val2 + next;
			next = ret / 10;
			ret = ret % 10;
			str += ('0' + ret);
		}
		if (next == 1)
			str += '1';
		reverse(str.begin(), str.end());

		return str;
	}
};

class Solution 
{
public:
// 这⾥的传值返回拷⻉代价就太⼤了
vector<vector<int>> generate(int numRows) 
{
   vector<vector<int>> vv(numRows);
   for(int i = 0; i < numRows; ++i)
   {
       vv[i].resize(i+1, 1);
   }
   for(int i = 2; i < numRows; ++i)
   {
      for(int j = 1; j < i; ++j)
      {
         vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
      }
   }
   return vv;
}
};

2.5.2移动构造和移动赋值

右值引用如何解决返回的对象是局部变量的问题?
1. 移动构造函数是一种构造函数,类似拷贝构造,要求第一个参数必须是类类型的引用,第一个参数必须是右值引用,拷贝构造是左值引用,如果有其他参数,必须有缺省值
2. 移动赋值是一个赋值运算符的重载,他跟拷贝赋值构成函数重载,类似拷贝赋值函数,移动赋值函数要求第一个参数是该类类型的引用,但是不同的是要求这个参数是右值引用
3. 对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类,移动构造和移动赋值才有意义,因为移动构造和移动赋值的第一个参数都是右值引用的类型,它的本质是要“窃取”引用的右值对象的资源,而不是像拷贝构造和拷贝赋值那样去拷贝资源,从提高效率

void swap(string& ss)
{
	::swap(_str, ss._str);
	::swap(_size, ss._size);
	::swap(_capacity, ss._capacity);
}

// 移动构造
string(string&& s)
{
	// 右值引用本身具有左值的属性
	// 因为要和this交换,左值可以修改值,右值不可以修改值
	cout << "string(string&& s) -> 移动构造" << endl;
	swap(s);
	// 交换需要的资源,转移掠夺你的资源
}

在这里插入图片描述

int main()
{
	string s1("111");
	string s2 = s1;
	string s3 = string("222");
    // s1本来的地址是xxxa510,现在s5的地址是a510
    // 说明移动构造掠夺了s1的资源给了s5
	string s5 = move(s1);

	return 0;
}

在这里插入图片描述

2.5.3右值引用和移动语义解决传值返回问题

2.5.3.1右值对象构造,只有拷贝构造,没有移动构造的场景->拷贝构造

1. vs2019debug下,左边为不优化的场景,传值返回会发生拷贝构造,会产生临时对象,临时对象再拷贝构造ret,右边的场景是直接优化为一次拷贝构造
2.vs2019release和vs2022下,会直接将str对象的构造,str拷贝构造临时对象,临时对象拷贝构造ret对象,合三为一,变为直接构造。本质就是str对象本质是ret对象的引用,底层是指针,str对象和ret对象的地址是一样的,所以就没有拷贝,没有构造,只有引用了
3. Linux下也是和2022一样的,编译时用 g++ test.cpp -fno-elide-constructors 的方式关闭构造优化,就是两次拷贝构造

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

在这里插入图片描述

2.5.3.2右值对象构造,有拷贝构造,也有移动构造的场景->移动构造

1. 传值返回会被编译器识别为右值,图2展示了vs2019 debug环境下编译器对拷贝的优化,左边为不优化的情况下,两次移动构造,右边为编译器优化的场景下连续步骤中的拷贝合二为一变为一次移动构造
2. 需要注意的是在vs2019的release和vs2022的debug和release,会直接将str对象的构造,str移动构造临时对象,临时对象移动构造ret对象,合三为一,变为直接构造。

在这里插入图片描述

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

2.5.3.3右值对象赋值,只有拷贝构造和拷贝赋值,没有移动构造和移动赋值的场景->拷贝构造和拷贝赋值

1. 左图不优化,一次拷贝构造,一次拷贝赋值,右图优化为直接拷贝赋值,str是临时对象的别名,直接用临时对象拷贝赋值ret

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

2.5.3.4右值对象赋值,既有拷贝构造和拷贝赋值,也有移动构造和移动赋值的场景->移动构造和移动赋值

1. 需要注意的是在vs2019的release和vs2022的debug和release,下面代码会进一步优化,直接构造要返回的临时对象,str本质是临时对象的引用,底层角度用指针实现。运行结果的角度,我们可以看到str的析构是在赋值以后,说明str就是临时对象的别名。(临时对象的生命周期只存在于当前行,赋值完之后就会销毁)
2. 如果是传值返回并且是右值对象,会移动你的资源,不用拷贝了

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

2.5.3.5 移动构造和移动赋值的意义

1. 深拷贝,使用移动构造和移动赋值,不需要进行大量的拷贝,只需要交换指针即可
2.浅拷贝,使用移动构造和移动赋值,就是锦上添花,其实传值返回要拷贝的很少,比如Date日期类,拷贝12个字节,pair< int , int >拷贝8个字节

在这里插入图片描述

3.面试经常问到的

1. 左值引用和右值引用的最终目的是减少拷贝提高效率
2.左值引用还可以修改参数和返回值,比如输出型参数(在函数体内修改参数可以影响实参)和operator

在这里插入图片描述
3. 左值引用的不足:
部分函数返回场景,只能传值返回,不能左值引用返回,比如当前函数的局部对象,出了当前函数的作用域,生命周期就到了,就销毁了,不能左值引用返回,只能传值返回
解决方案1,2,3:
1.不用返回值,用输出型参数解决问题,不足:牺牲了可读性
2.编译器的优化
3.右值引用和移动语义

更早的编译器只能使用这种输出型参数,避免大量拷贝数据

class Solution 
{
public:
  void generate(int numRows, vector<vector<int>>& vv) 
  {
	vector<vector<int>> vv(numRows);
	for (int i = 0; i < numRows; ++i)
	{
		vv[i].resize(i + 1, 1);
	}
	for (int i = 2; i < numRows; ++i)
	{
		for (int j = 1; j < i; ++j)
		{
			vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
		}
	}
  }
};

int main()
{
	// vector<vector<int>> ret = Solution().generate(100);
	vector<vector<int>> ret;
	Solution().generate(100, ret);
	// 输出型参数可以解决这个问题,传引用过去,就不需要拷贝了

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot常用注解深度解析:从入门到精通

今天&#xff0c;这篇文章带你将深入理解Spring Boot中30常用注解&#xff0c;通过代码示例和关系图&#xff0c;帮助你彻底掌握Spring核心注解的使用场景和内在联系。 一、启动类与核心注解 1.1 SpringBootApplication 组合注解&#xff1a; SpringBootApplication Confi…

生成式AI安全最佳实践 - 抵御OWASP Top 10攻击 (下)

今天小李哥将开启全新的技术分享系列&#xff0c;为大家介绍生成式AI的安全解决方案设计方法和最佳实践。近年来生成式 AI 安全市场正迅速发展。据IDC预测&#xff0c;到2025年全球 AI 安全解决方案市场规模将突破200亿美元&#xff0c;年复合增长率超过30%&#xff0c;而Gartn…

git:恢复纯版本库

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

蓝桥杯python基础算法(2-1)——排序

目录 一、排序 二、例题 P3225——宝藏排序Ⅰ 三、各种排序比较 四、例题 P3226——宝藏排序Ⅱ 一、排序 &#xff08;一&#xff09;冒泡排序 基本思想&#xff1a;比较相邻的元素&#xff0c;如果顺序错误就把它们交换过来。 &#xff08;二&#xff09;选择排序 基本思想…

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理

【1】引言 前序学习进程中&#xff0c;对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像&#xff0c;每个像素点上的BGR值为三个整数&#xff0c;因为是三通道图像&#xff1b;对于灰度图像&#xff0c;各个像素上的BGR值是一个整数&#xff0c;因为这是单通…

Slint的学习

Slint是什么 Slint是一个跨平台的UI工具包&#xff0c;支持windows,linux,android,ios,web&#xff0c;可以用它来构建申明式UI,后端代码支持rust,c,python,nodejs等语言。 开源地址&#xff1a;https://github.com/slint-ui/slint 镜像地址&#xff1a;https://kkgithub.com/…

惰性函数【Ⅱ】《事件绑定的自我修养:从青铜到王者的进化之路》

【Ⅱ】《事件绑定的自我修养&#xff1a;从青铜到王者的进化之路》 1. 代码功能大白话&#xff08;给室友讲明白版&#xff09; // 青铜写法&#xff1a;每次都要问浏览器"你行不行&#xff1f;" function addEvent青铜版(element, type, handler) {if (window.add…

Unity飞行代码 超仿真 保姆级教程

本文使用Rigidbody控制飞机&#xff0c;基本不会穿模。 效果 飞行效果 这是一条优雅的广告 如果你也在开发飞机大战等类型的飞行游戏&#xff0c;欢迎在主页搜索博文并参考。 搜索词&#xff1a;Unity游戏(Assault空对地打击)开发。 脚本编写 首先是完整代码。 using System.Co…

基于微信小程序的私家车位共享系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

C++编程语言:抽象机制:模板(Bjarne Stroustrup)

目录 23.1 引言和概观(Introduction and Overview) 23.2 一个简单的字符串模板(A Simple String Template) 23.2.1 模板的定义(Defining a Template) 23.2.2 模板实例化(Template Instantiation) 23.3 类型检查(Type Checking) 23.3.1 类型等价(Type Equivalence) …

多线程的常用方法

getName和setName方法 注意点 setName方法最好放在线程启动之前 最好在线程启动之前修改名字&#xff0c;因为线程启动之后&#xff0c;如果执行过快的话&#xff0c;那么在调用 setName() 之前线程可能就已经结束了 MyThread t1 new MyThread("haha"); t1.setNa…

C++继承的基本意义

文章目录 一、继承的本质和原理二、重载、隐藏和覆盖三、基类与派生类的转换 一、继承的本质和原理 继承的本质&#xff1a;a. 代码的复用 b. 类和类之间的关系&#xff1a; 组合&#xff1a;a part of… 一部分的关系 继承&#xff1a;a kind of… 一种的关系 总结&#xff…

简单易懂的倒排索引详解

文章目录 简单易懂的倒排索引详解一、引言 简单易懂的倒排索引详解二、倒排索引的基本结构三、倒排索引的构建过程四、使用示例1、Mapper函数2、Reducer函数 五、总结 简单易懂的倒排索引详解 一、引言 倒排索引是一种广泛应用于搜索引擎和大数据处理中的数据结构&#xff0c;…

FinRobot:一个使用大型语言模型的金融应用开源AI代理平台

“FinRobot: An Open-Source AI Agent Platform for Financial Applications using Large Language Models” 论文地址&#xff1a;https://arxiv.org/pdf/2405.14767 Github地址&#xff1a;https://github.com/AI4Finance-Foundation/FinRobot 摘要 在金融领域与AI社区间&a…

Docker使用指南(一)——镜像相关操作详解(实战案例教学,适合小白跟学)

目录 1.镜像名的组成 2.镜像操作相关命令 镜像常用命令总结&#xff1a; 1. docker images 2. docker rmi 3. docker pull 4. docker push 5. docker save 6. docker load 7. docker tag 8. docker build 9. docker history 10. docker inspect 11. docker prune…

Qt跨屏窗口的一个Bug及解决方案

如果我们希望一个窗口覆盖用户的整个桌面&#xff0c;此时就要考虑用户有多个屏幕的场景&#xff08;此窗口要横跨多个屏幕&#xff09;&#xff0c;由于每个屏幕的分辨率和缩放比例可能是不同的&#xff0c;Qt底层在为此窗口设置缩放比例&#xff08;DevicePixelRatio&#xf…

Linux 传输层协议 UDP 和 TCP

UDP 协议 UDP 协议端格式 16 位 UDP 长度, 表示整个数据报(UDP 首部UDP 数据)的最大长度如果校验和出错, 就会直接丢弃 UDP 的特点 UDP 传输的过程类似于寄信 . 无连接: 知道对端的 IP 和端口号就直接进行传输, 不需要建立连接不可靠: 没有确认机制, 没有重传机制; 如果因…

安全实验作业

一 拓扑图 二 要求 1、R4为ISP&#xff0c;其上只能配置IP地址&#xff1b;R4与其他所有直连设备间均使用共有IP 2、R3-R5-R6-R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b; 4、所有设备均可访问R4的环回&#x…

防御保护:安全策略配置

目录 一、实验拓扑 二、实验要求 ​编辑 三、要求分析 四、实验配置 前置配置 1.配置vlan与access、truck接口 2.进入web界面进行配置 3.安全策略的配置 3.1实现实验需求2(办公区PC在工作日时间(周一至周五,早8晚6)可以正常访问OA Server,其他时间不允许) 新建地址…

第一个Qt开发实例(一个Push Button按钮和两个Label)【包括如何在QtCreator中创建新工程、代码详解、编译、环境变量配置、测试程序运行等】

目录 Qt开发环境QtCreator的安装、配置在QtCreator中创建新工程在Forms→mainwindow.ui中拖曳出我们要的图形按钮查看拖曳出按钮后的代码为pushButton这个图形添加回调函数编译工程关闭开发板上QT的GUI(选做)禁止LCD黑屏(选做)设置Qt运行的环境变量运行Qt程序如何让程序在系统启…