【 C++ 】C++11的初步学习

news2024/11/28 4:30:12

目录

​编辑

一、列表初始化

1.支持内置类型

2.支持自定义类型

 二、initializer-list

三、右值引用和移动语义

1.左值和右值

a)左值

b)右值

2.左值引用和右值引用

a)左值引用

b)右值引用

c)总结

d)引用延长生命周期

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

4.移动构造和移动赋值

a)移动构造

b)移动赋值

5.引用折叠

a)typedef引用的引用--引用折叠

b)类模板引用的引用--引用折叠

1).左值引用类模板

2).右值引用类模板--万能引用

c)引用的推导

6.完美转发

a)完美转发


一、列表初始化

在C++11中,我们开始支持用列表来初始化对象

1.支持内置类型

int a1 = { 2 };
int a2 = 2;

上面两种初始化,它们的效果实际都相同。

2.支持自定义类型

class BirthDay
{
public:
	BirthDay(int y=1949, int m=10, int d=1)
		:_year(y)
		,_month(m)
		,_day(d)
	{
		cout << "BirthDay(int y=1949, int m=10, int d=1)--默认构造" << endl;
	}

	BirthDay(const BirthDay& birday)
		:_year(birday._year)
		, _month(birday._month)
		, _day(birday._day)
	{
		cout << "BirthDay(const BirthDay& birday)--拷贝构造" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	BirthDay a={ 2024,10,27 };
	return 0;
}

在上面的例子中,我们就使用到了列表初始化。

BirthDay a={ 2024,10,27 }的本质其实是,先用{ 2024,10,27 }构造一个临时对象,再用这个临时对象去拷贝构造a。但是我们的编译器对这一步进行了优化,合二为一,省去了拷贝构造这一步。

可以看到这里只调用了构造函数,并没有调用拷贝构造。

在这里b引用的其实是{ 2014,10,27 }所构造出来的临时对象,而临时对象具有常性,所以要加const。

我们还可以直接去掉=来进行赋值:

可以发现有了列表初始化后,我们写代码会方便很多:

 二、initializer-list

我们需要注意列表初始化和initializer_list的区别,虽然我们有了列表初始化,但是当我们想要对容器进行初始化还是不太方便,而initializer_list就可以用多个值对容器进行初始化:

而initializer_list其实是一个类模板,当我们用花括号列表去给一个容器,那么这就会被识别为initializer_list类型:

 它的底层其实是数组,内部有两个指针指向开始和结束,因此它支持迭代器遍历,我们可以看看它所拥有的成员:

当一个容器支持initializer_list构造函数,那么就意味着它可以用多个值组成的花括号来初始化。而STL中的容器支持{x1,x2,x3...}这种形式来初始化,其实就是通过initializer_list的构造函数来实现的。

在这里我们需要注意下面两个写法,它们的效果虽然相同,但是语义有差别。

三、右值引用和移动语义

1.左值和右值

a)左值

左值是⼀个表示数据的表达式(如变量名或解引用的指针),存储在内存中。英文简写为lvalue,是left value的简写。在现代C++中,lvalue又被解释为loactor value的缩写,可意为存储在内存中、有明确存储地址可以取地址的对象。

  • 我们可以获取左值的地址,可以对其进行赋值。但是被const所修饰的左值只能取地址,不能赋值
  • 左值可以出现在赋值符号的左边或者右边


b)右值

右值也是一个表示数据的表达式,比如字面值常量、表达式求值过程中创建的临时对象等,它的英文简写是rvalue,传统认为是right value的简写。在现代c++中,它也被解释为read value意为只读值,即可以提供数据值,但不能寻址不能修改。

在C++中,右值又被分为纯右值和将亡值:

纯右值:指的是那些不绑定到内存地址的临时值,其生命周期通常很短,通常在表达式计算结束时就被丢弃。

例如字面量:整数10、布尔值true以及调用一个函数非引用的返回值。

将亡值:将亡值表示的是那些即将被销毁但仍持有资源的对象。这些对象通常是通过右值引用返回的函数调用、std::move的返回值等表达式产生的。将亡值允许在对象被销毁之前,将其资源(如动态分配的内存、文件句柄等)高效地转移到另一个对象中,这是通过移动语义实现的。

  • 右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,即不能被赋值修改。
  • 不能取地址

可以看到,当我们对右值进行取地址时,编译器进行了报错,并且明确表示&取地址符号要求左值。


2.左值引用和右值引用

type& a;//左值引用
type&& b;//右值引用

a)左值引用

给左值起别名:

int main()
{
	//p a b *p s s[0]皆为左值
	int* p = new int(0);
	int a = 1;
	const int b = a;
	*p = 10;
	string s("12345");
	s[0] = 'x';

	//它们都能取地址
	cout << &p << endl;
	cout << &a << endl;
	cout << &b << endl;
	cout << &(*p) << endl;
	cout << &s << endl;
	cout << (void*)&s[0] << endl;

	//左值引用
	int*& rp = p;
	int& ra = a;
	const int& rb = b;
	int& rpp = *p;
	string& ps = s;
	char& ps0 = s[0];

	cout << endl;
}

b)右值引用

给右值起别名

int Add(int a, int b)
{
	return a + b;
}

int main()
{
	double x = 1.1, y = 2.2;

	//以下皆为右值
	/*
	10;
	x + y;
	Add(1, 2);
	string("123");
	*/

	//不能对右值进行取地址
	/*cout << &10 << endl;
	cout << &(x + y) << endl;
	cout << &(Add(1, 2)) << endl;
	cout << &string("123") << endl;*/

	int&& r10 = 10;
	double&& rxy = (x + y);
	int&& radd = Add(1, 2);
	string&& rstr = string("123");
	return 0;
}

note:当我们给右值引用之后,这里的r10是右值引用,但是r10的属性是左值。这意味着,通过右值引用,我们可以做到修改右值。因为r10此时是右值,我们可以修改r10,也可以对它进行取地址。这一点会在后面有更详细的介绍和使用,我们先简单看一下:

可以看到这里我们能修改r10,也能对它进行取地址


c)总结

  • 左值引用不能直接引用右值,但是const左值引用可以引用右值
  • 右值引用不能直接引用左值,但是右值引用可以引用move(左值)
  • 值得注意的是,当对一个左值进行move的时候,结果是右值,但是这个左值还是左值,没有发生改变。这就类似于,int a=0;  double da=(double)a;这里的a还是int类型,只是进行了强转,结果是double类型,但是a本身的类型没有发生改变。

当我们直接用右值引用绑定左值时,编译器会进行报错

我们可以使用move对c进行强转,让右值引用可以绑定被move后的左值 

这里的move其实是库里面的函数模板,本质是进行强制类型转换

  •  当一个右值被右值引用绑定之后,右值引用的属性是左值

此时可以看到r10作为右值引用,但是r10的属性是左值,所以r10可以取地址也可以修改。

  • 语法角度,左值引用和右值引用都是起别名,不例外开空间。但是在汇编底层的角度来看,左值引用和右值引用其实都是用指针来进行实现。

d)引用延长生命周期

匿名对象和临时对象,它们的生命周期只在当前行。从下面这个例子可以看到匿名对象会在当前行结束后被析构。

 

但是通过引用,我们可以延长它的生命周期:

右值引用延长生命周期

const左值引用延长生命周期


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

通常左值引用使用于函数中传参和左值引用传返回值时减少拷贝,还可以修改实参和修改返回对象的价值。

但是在一些场景中,我们不能使用传左值引用返回来解决拷贝效率问题:

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

在上面这个例子中,我们不能使用传左值引用返回,也不能使用传右值引用返回来解决vv出了函数所带来的拷贝效率问题。

问题所在:

那么我们将如何解决这个问题呢?---移动构造和移动赋值


4.移动构造和移动赋值

移动构造函数是⼀种构造函数,类似拷贝构造函数,移动构造函数要求第⼀个参数是该类类型的引
⽤,但是不同的是要求这个参数是右值引用,如果还有其他参数,额外的参数必须有缺省值。
移动赋值是⼀个赋值运算符的重载,他跟拷贝赋值构成函数重载,类似拷贝赋值函数,移动赋值函
数要求第⼀个参数是该类类型的引用,但是不同的是要求这个参数是右值引用。
对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类,移动构造和移动赋值才有 意义,因为移动构造和移动赋值的第⼀个参数都是右值引用的类型,他的本质是要“窃取”引⽤的
右值对象的资源,而不是像拷贝构造和拷⻉赋值那样去拷贝资源,从提高效率。下面的bit::string 样例实现了移动构造和移动赋值,我们需要结合场景理解。

a)移动构造

当我们在用临时对象给目标对象拷贝构造的时候,这一过程其实是有点浪费的,因为临时对象拷贝完之后就会被销毁,那我们其实可以将临时对象的资源直接给我们的目标对象。怎么给呢?通过交换的方法:

我们用string来举一个例子加深理解:

我们原先是用局部对象拷贝构造给匿名对象,匿名对象拷贝构造给目标对象

但是通过移动构造我们可以直接省掉临时对象到目标对象的这一拷贝过程:

struct String
{
	//...
	// 这里省略String类的具体模拟实现
	//....

	void Swap( String& y)
	{
		std::swap(_size, y._size);
		std::swap(_capacity, y._capacity);
		std::swap(_arr, y._arr);
	}

	//移动构造
	String(String&& temp)//temp可以为临时对象或者匿名对象
	{
		Swap(temp);
	}

public:
	int _size;
	int _capacity;
	int* _arr;
};

这里通过移动构造,我们可以省略掉临时对象到目标对象的拷贝构造:

此时临时对象有我们所需要的资源,而我们目标对象刚刚创建出来什么都没有,与临时对象交换也就是掠夺临时对象的资源。然后临时对象带着目标对象交换过来的空空如也去销毁。

这时候将右值引用的属性设置为左值的意义也体现出来了,这样我们就可以通过右值引用对右值进行修改。

此时我们没有通过拷贝构造再生成一份资源,而是直接掠夺临时对象的资源。


但其实在没有移动构造的情况下,我们也看不到两次拷贝构造,这是因为编译器对此进行了优化:

在vs2019的debug下,编译器会直接用局部对象去拷贝构造我们的目标对象


在release情况下,甚至连这一次拷贝构造也没有,直接就是一次目标对象的构造。

这个底层其实就是vv是arr的引用,所以可以做到只用一次构造不用多余的拷贝行为。


b)移动赋值

虽然编译器有优化,但是还有一种情况:

这种情况就不是调用拷贝构造而是赋值,赋值也需要进行拷贝,而此时赋值也可以用移动赋值解决拷贝问题:


 在以前的c++版本中,下面的拷贝构造可以优化为直接构造,但是这个拷贝赋值还是优化不了

现在在C++11中,拷贝赋值就可以优化为移动赋值

此时就完全解决了对于右值产生的拷贝问题。


5.引用折叠

在C++中我们不能直接定义引用的引用,int& && ra=a。这样的写法是错误的,编译器会进行报错,通过模板或 typedef中的类型操作可以构成引用的引用。而在这里C++对引用的引用给了一个引用折叠的规则,即右值引用的右值引用折叠成右值引用,所有其他组合均折叠成左值引用。

a)typedef引用的引用--引用折叠

int main()
{
	typedef int& lf;
	typedef int&& rf;
	int n = 0;

	lf& r1 = n;//r1:左值引用的左值引用----左值引用
	lf&& r2 = n;//r2:左值引用的右值引用----左值引用

	rf& r3 = n;//r3:右值引用的左值引用----左值引用
	rf&& r4 = move(n);//r4:右值引用的右值引用---右值引用

	return 0;
}

b)类模板引用的引用--引用折叠

1).左值引用类模板
template<class T>
void f1(T& x)
{

}

int main()
{
	int n = 0;

	//没有发生折叠--实例化为void f1(int& x)
	f1<int>(n);
	f1<int>(0);//编译器报错,实例化为void f1(int& 0),左值引用不能引用右值

	//发生了折叠--左值引用的左值引用--实例化为void f1(int& x)
	f1<int&>(n);
	f1<int&>(0);//编译器报错,实例化为void f1(int& 0),左值引用不能引用右值

	//发生了折叠--右值引用的左值引用--实例化为void f1(int& x)
	f1<int&&>(n);
	f1<int&&>(0);//编译器报错,实例化为void f1(int& 0),左值引用不能引用右值

	//发生了折叠--左值引用的左值引用--实例化为void f1(const int& x)
	f1<const int&>(n);
	f1<const int&>(0);//编译器不报错,因为const 左值引用可以绑定右值

	//发生了折叠--右值引用的左值引用--实例化为void f1(const int& x)
	f1<const int&&>(n);
	f1<const int&&>(0);//编译器不报错,因为const 左值引用可以绑定右值

	return 0;
}

2).右值引用类模板--万能引用
template<class T>
void f2(T&& x)
{

}

int main()
{

	int n = 0;

	//没有发生折叠--实例化为void f2(int&& x)
	f2<int>(n);//编译器报错,实例化为void f2(int&& n),右值引用不能引用左值
	f2<int>(0);

	//发生了折叠--右值引用的左值引用--实例化为void f2(int& x)
	f2<int&>(n);
	f2<int&>(0);//编译器报错,实例化为void f2(int& 0),左值引用不能引用右值

	//发生了折叠--右值引用的右值引用--实例化为void f2(int&& x)
	f2<int&&>(n);//编译器报错,实例化为void f2(int&& x),右值引用不能引用左值
	f2<int&&>(0);

	//发生了折叠--右值引用的左值引用--实例化为void f2(const int& x)
	f2<const int&>(n);
	f2<const int&>(0);//编译器不报错,因为const 左值引用可以绑定右值

	//发生了折叠--右值引用的右值引用--实例化为void f2(const int&& x)
	f2<const int&&>(n);//编译器报错,实例化为void f2(const int&& n),const 右值引用不能引用左值
	f2<const int&&>(0);

	return 0;
}

c)引用的推导

1)

template<class T>
void Func(T&& t)
{
	int a = 10;
	T x = a;
	x++;
	cout << &a << endl;
	cout << &x << endl;
}

int main()
{
	Func(10);
	return 0;
}

在上面的情况中,10为右值,则编译器会将T推导为int,此时就相当于int a=10;int b=10;打印它们的地址会出来不同的结果:


2)

template<class T>
void Func(T&& t)
{
	int a = 10;
	T x = a;
	x++;
	cout << &a << endl;
	cout << &x << endl;
}

int main()
{
	//Func(10);//右值,T推导为int
	int b;
	Func(b);
	return 0;
}

在这种情况中,b为左值,因此T会被推到为int&,相当于int a=10;int& x=a;此时打印它们的地址会出来相同的结果。


3)

template<class T>
void Func(T&& t)
{
	int a = 10;
	T x = a;
	x++;
	cout << &a << endl;
	cout << &x << endl;
}

int main()
{
	int b;

	//Func(10);//右值,T推导为int

	//Func(b);//左值,T推导为int&

	Func(move(b));

	return 0;
}

b本来是左值,被move过后传给t的是右值类型,因此T会被推导为int,得到int x=a;打印结果会发现地址不同,并且x可以修改。


4)

int main()
{
	int b;

	//Func(10);//右值,T推导为int

	//Func(b);//左值,T推导为int&

	//Func(move(b));//右值,T推导为int

	const int cb=1;
	Func(cb);

	return 0;
}

此时cb的类型为const 左值,那么T被推导为const int&,即const int& x=a;此时编译器会在x++处进行报错,因为x不能修改,但是打印它们的地址会发现相同的结果。

它们地址相同:


5)

int main()
{
	int b;

	//Func(10);//右值,T推导为int

	//Func(b);//左值,T推导为int&

	//Func(move(b));//右值,T推导为int

	const int cb=1;
	//Func(cb);//const左值,T被推导为const int&,不能修改但是可以求地址

	Func(move(cb));

	return 0;
}

在这种情况中,cb本是左值,但是move后传给Func函数是const int&&,则T会被推导为const int&&,这种情况x不能修改也不能取地址。


6.完美转发

先来看看下面这段代码:

void Fun(int& x) { cout <<x<< "左值引用" << endl; }
void Fun(const int& x) { cout<<x << "const 左值引用" << endl; }
void Fun(int&& x) { cout<<x << "右值引用" << endl; }
void Fun(const int&& x) { cout<<x << "const 右值引用" << endl; }

template<class T>
void Function(T&& t)
{
	Fun(t);
}

int main()
{
	Function(1);//在Function函数中实例化为右值引用,T为int,即int&&

	int a = 2;
	Function(a);//在Function函数中实例化为左值引用,T为int&,即int&

	a++;
	int& b = a;
	Function(b);//在Function函数中实例化为左值引用,T为int&,即int&

	int&& c = 4;
	Function(move(c));//在Function函数中实例化为右值引用,T为int&&,即int&&

	a+=2;
	const int& d = a;
	Function(d);//在Function函数中实例化为左值引用,T为const int&,即const int&

	const int&& e = 6;
	Function(move(e));//在Function函数中实例化为右值引用,T为const int&&,即const int&&

	return 0;
}

我们再来看看结果:

结果是这6个引用全都变成了左值引用。

右值引用的变量,其属性是左值引用。

int&& ra=1;在这句代码中,ra是右值引用,但是ra的属性是左值,因此ra可以取地址和被修改。

理解了这一点,我们就能明白为什么上面6个引用最后全部变成了左值引用,这是因为不管t是左值引用还是右值引用,t的属性永远都是左值,因此将t向下传给另外一个函数时,编译器会自动推导为左值引用。

那么如何解决这一点呢?

编译器给出了完美转发


a)完美转发

完美转发其实是一个模板,如果是左值引用,它将直接返回左值引用不加任何修改。如果不是左值引用,它将返回右值引用。它主要还是通过引用折叠的方式来进行使用。

我们使用完美转发再来运行一下上面的代码试试

#include<utility>

void Fun(int& x) { cout <<x<< "左值引用" << endl; }
void Fun(const int& x) { cout<<x << "const 左值引用" << endl; }
void Fun(int&& x) { cout<<x << "右值引用" << endl; }
void Fun(const int&& x) { cout<<x << "const 右值引用" << endl; }

template<class T>
void Function(T&& t)
{
	Fun(forward<T>(t));//完美转发
}

int main()
{
	Function(1);//在Function函数中实例化为右值引用,T为int,即int&&

	int a = 2;
	Function(a);//在Function函数中实例化为左值引用,T为int&,即int&

	a++;
	int& b = a;
	Function(b);//在Function函数中实例化为左值引用,T为int&,即int&

	int&& c = 4;
	Function(move(c));//在Function函数中实例化为右值引用,T为int&&,即int&&

	a+=2;
	const int& d = a;
	Function(d);//在Function函数中实例化为左值引用,T为const int&,即const int&

	const int&& e = 6;
	Function(move(e));//在Function函数中实例化为右值引用,T为const int&&,即const int&&

	return 0;
}

此时再来看看结果:




如果这篇文章有帮助到你,请留下您珍贵的点赞、收藏+评论,这对于我将是莫大的鼓励!学海无涯,共勉!😘😊😗💕💕😗😊😘




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

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

相关文章

云计算平台上的DevOps实践

文章目录 什么是DevOps云计算平台上的DevOps优势自动化部署弹性伸缩地理分布 实施DevOps的关键组件版本控制系统持续集成/持续交付工具配置管理工具监控和日志管理 实践案例使用AWS CodePipeline进行持续集成/持续交付利用AWS Auto Scaling实现弹性使用AWS CloudFormation进行基…

C++引用类型变量

引用变量的主要用途是用作函数的形参。这样函数将使用原始数据&#xff0c;而不是副本。除指针之外&#xff0c;引用也为处理大型结构提供了一种非常方便的途径。 在C中使用&符号标识引用。也就是说C给&符号赋予了另一个含义&#xff0c;将其用来声明引用。 引用的声…

「C/C++」C/C++预处理 之 X宏(X Macro)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

数据结构————map,set详解

今天带来map和set的详解&#xff0c;保证大家分清楚 一&#xff0c;概念 map和set是一种专门用来搜索的容器或数据结构 map能存储两个数据类型&#xff0c;我们称之为<key-value>模型 set只能存储一个数据类型&#xff0c;我们称之为纯<key>模型 它们的效率都非…

APISQL企业版离线部署教程

针对政务、国企、医院、军工等内网物理隔离的客户&#xff0c;有时需要多次摆渡才能到达要安装软件的服务器。本教程将指导您使用Linux和Docker Compose编排服务&#xff0c;实现APISQL的离线部署。 准备 准备一台Linux(x86_64)服务器。 安装Docker Engine&#xff08;推荐版本…

DC-1渗透测试

DC1 五个flag的拿取&#xff08;截图是五个flag里面的内容&#xff09; 注意事项&#xff1a;kali的用户名&#xff1a;root 密码&#xff1a;kali 注意&#xff1a;DC1 只要开机服务就起来了 思路&#xff1a;信息收集—> 寻找漏洞—> 利用漏洞(sql注入,文件上传漏洞…

uniapp的IOS证书申请(测试和正式环境)及UDID配置流程

1.说明 本教程只提供uniapp在ios端的证书文件申请&#xff08;包含正式环境和开发环境&#xff09;、UDID配置说明&#xff0c;请勿用文档中的账号和其他隐私数据进行测试&#xff0c;请勿侵权&#xff01; 2.申请前准备 证书生成网站&#xff1a;苹果应用上传、解析&#x…

vxe-table 表格中使用输入框、整数限制、小数限制,单元格渲染数值输入框

Vxe UI vue vxe-table 表格中使用输入框、整数限制、小数限制&#xff0c;单元格渲染数值输入框 在 vxe-table v4.7 单元格中渲染有非常多的方式&#xff0c;可以使用自带的组件&#xff0c;也可以已使用第三方的组件 element ui 之类的。本章介绍如果使用自带的输入框&#x…

Negative Sampling in Recommendation: A Survey and Future Directions

目录 Introduction分类&#xff1a;静态负采样策略动态负采样策略对抗负采样策略重要性重加权策略知识增强负采样策略多种推荐场景的负采样 Introduction 传统的推荐算法通常关注用户的正面历史行为&#xff0c;而忽视了负面反馈在理解用户兴趣中的重要作用。负面采样是推荐系…

Java IO 模型

I/O 何为 I/O? I/O&#xff08;Input/Output&#xff09; 即输入&#xff0f;输出 。 我们先从计算机结构的角度来解读一下 I/O。 根据冯.诺依曼结构&#xff0c;计算机结构分为 5 大部分&#xff1a;运算器、控制器、存储器、输入设备、输出设备。 输入设备&#xff08;比…

MFC实现以不规则PNG图片作为窗口背景

效果图 显示的不规则PNG图片 头文件 #pragma once #include <gdiplus.h> #pragma comment (lib,"Gdiplus.lib")// CShowBack 对话框class CShowBack : public CDialogEx {DECLARE_DYNAMIC(CShowBack) public:CShowBack(CWnd* pParent nullptr); // 标准构…

数字IC开发:布局布线

数字IC开发&#xff1a;布局布线 前端经过DFT&#xff0c;综合后输出网表文件给后端&#xff0c;由后端通过布局布线&#xff0c;将网表转换为GDSII文件&#xff1b;网表文件只包含单元器件及其连接等信息&#xff0c;GDS文件则包含其物理位置&#xff0c;具体的走线&#xff1…

HarmonyOS 5.0应用开发——Navigation实现页面路由

【高心星出品】 文章目录 Navigation实现页面路由完整的Navigation入口页面子页面 页面跳转路由拦截其他的 Navigation实现页面路由 Navigation&#xff1a;路由导航的根视图容器&#xff0c;一般作为页面&#xff08;Entry&#xff09;的根容器去使用&#xff0c;包括单页面&…

Flink CDC系列之:学习理解核心概念——Data Pipeline

Flink CDC系列之&#xff1a;学习理解核心概念——Data Pipeline 数据管道sourcesink管道配置Table IDroutetransform案例 数据管道 由于 Flink CDC 中的事件以管道方式从上游流向下游&#xff0c;因此整个 ETL 任务被称为数据管道。 管道对应于 Flink 中的一系列操作。 要描…

25届电信保研经验贴(清华大学电子工程系,工程硕博)

个人背景 学校&#xff1a;中九 专业&#xff1a;电子信息工程 加权&#xff1a;92.89 绩点&#xff1a;3.91/4.0 rank&#xff1a;前五学期rank2/95&#xff0c;综合排名rank1&#xff08;前六学期和综合排名出的晚&#xff0c;实际上只用到了前五学期&#xff09; 科研…

安卓取消触摸屏幕的指针效果

在安卓系统中&#xff0c;取消触摸屏幕的指针效果&#xff08;通常指开发者模式下的屏幕点按反馈显示或指针位置显示&#xff09;可以通过ADB&#xff08;Android Debug Bridge&#xff09;命令来实现。以下是具体的步骤和命令&#xff1a; 使用ADB命令取消触摸屏幕的指针效果 …

数据库基础介绍

前言&#xff1a; 在当今信息化、数字化的时代&#xff0c;数据库是支撑一切信息系统的核心基础设施。无论是金融机构的账户管理、电商平台的商品库存&#xff0c;还是社交媒体的用户信息&#xff0c;数据库都在背后扮演着关键角色数据库不仅用于存储和管理数据&#xff0c;更…

使用AMD GPU和LangChain构建问答聊天机器人

Question-answering Chatbot with LangChain on an AMD GPU — ROCm Blogs 作者&#xff1a;Phillip Dang 2024年3月11日 LangChain是一个旨在利用语言模型强大功能来构建前沿应用程序的框架。通过将语言模型连接到各种上下文资源并基于给定的上下文提供推理能力&#xff0c;L…

一篇文章总结 SQL 基础知识点

1. 官方文档 MySQL&#xff1a;https://dev.mysql.com/doc/refman/8.4/en/ SQL Server&#xff1a;What is SQL Server? - SQL Server | Microsoft Learn Oracle&#xff1a;https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/loe.html 2. 术语 SQL S…

【Java数据结构】树】

【Java数据结构】树 一、树型结构1.1 概念1.2 特点1.3 树的类型1.4 树的遍历方式1.5 树的表示形式1.5.1 双亲表示法1.5.2 孩子表示法1.5.3 孩子双亲表示法1.5.4 孩子兄弟表示法 二、树型概念&#xff08;重点&#xff09; 此篇博客希望对你有所帮助&#xff08;帮助你了解树&am…