【C++进阶】之C++11的简单介绍(二)

news2024/10/24 21:06:30

在这里插入图片描述

📃博客主页: 小镇敲码人
💚代码仓库,欢迎访问
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞

【C++进阶】之C++11的简单介绍(二)

  • 新的类功能
    • 移动构造和移动赋值
      • 当某个类没有显式实现移动赋值和移动构造
        • 右值的自定义成员是否为右值
    • 强制生成默认函数的关键字--- default
    • 强制生成默认函数的关键字--- delete
    • 继承和多态中的final与override关键字
    • 可变参数模板的语法
    • 递归展开参数包
      • 可变参数模板的底层原理
      • 逗号表达式展开参数包
    • C++11中的emplace_back系列函数
      • emplace_back与push_back效率比较

新的类功能

移动构造和移动赋值

移动构造我们前面已经详细的讲述过了,这里不再重复的赘述,这里主要就移动赋值做一下阐述。

看下面的代码,思考有没有出现不必要的拷贝,如何优化?

#include"string.h"


int main()
{
	my_string::string ret1;

	ret1 = my_string::to_string(12345);

	cout << ret1 << endl;
	return 0;
}

在这里插入图片描述
利用移动拷贝优化,减少这一次不必要的拷贝:

   string& string::operator= (string&& str)
	{
		cout << "string& string::operator= (string && str)" << endl;
		swap(str);

		return *this;
	}

我们把临界资源转移减少了不必要的拷贝,同时把ret1的资源交给临界资源去释放。

在这里插入图片描述

  • 只有需要深拷贝的类需要移动构造和移动赋值方法,因为浅拷贝没有需要申请空间的资源,所耗时间不多。

当某个类没有显式实现移动赋值和移动构造

  1. 如果你实现了移动构造或者移动赋值,编译器不会再提供拷贝构造和拷贝赋值。

在这里插入图片描述
在这里插入图片描述
实现任何一个,编译器都会把拷贝构造和拷贝赋值删除。

  1. 如果你没有实现移动构造,且没有实现析构函数 、拷贝构造、拷贝赋值重载、移动赋值中的任
    意一个。那么编译器会默认生成一个移动构造,对于内置类型它是逐字节赋值;对于自定义类型,如果这个自定义类型自己实现了移动构造,则会去调用它的移动构造,如果没有,就调用拷贝构造。

在这里插入图片描述

  • A中有自定义类型变量,且实现了A的移动构造,需要在初始化列表显示掉该自定义类型的移动构造:

在这里插入图片描述

这里会有人质疑,可能你的x接收了一个右值,但是你能保证这个右值里面的自定义成员也是右值吗?这里答案是显然的,右值的所有成员都是右值,我们可以来验证一下:

右值的自定义成员是否为右值
#include<iostream>

using namespace std;



class B
{
public:
	B()
	{}
	B(B&& x)
	{
		cout << "B(B&& x)" << endl;
	}
};
class A
{
public:
	A()
	{}

	A(A&& x)
		:b(move(x.b))
	{
		cout << "A(A&& x)" << endl;
	}
public:
	int a = 3;
	B b;
};

int main()
{
	A a;
	A b(move(a));
	move(a).b = 3;
	cout << &move(a).b << endl;
}

这里我们将a move后,它返回一个右值,如果它的成员是左值,应该可以修改和取地址,最关键的是看取地址,因为const属性的变量也不允许修改:

在这里插入图片描述

  1. 如果你没有实现移动赋值重载,且没有实现析构函数 、拷贝构造、拷贝赋值重载、移动构造中的任
    意一个。那么编译器会默认生成一个移动赋值重载函数,对于内置类型它是逐字节赋值;对于自定义类型,如果这个自定义类型自己实现了移动赋值重载,则会去调用它的移动赋值重载,如果没有,就调用拷贝赋值。

在这里插入图片描述
如果显式的实现了移动赋值重载函数,类中有自定义类型成员,需要在函数中显式的调用它,减少拷贝。

在这里插入图片描述

强制生成默认函数的关键字— default

当我们在写一些方法的时候,会出现写了部分方法,部分默认方法被删除的情况,这种情况常见于构造函数和赋值构造函数中,比如当你显式的写了带参的构造函数,不带参的默认构造函数就会被删除,这个时候我们可以使用default,强制它生成。

class A
{
public:
	A(int data):
		data_(data)
	{

	}
private:
	int data_;
};

int main()
{
	A a;
}

报错:

在这里插入图片描述

强制生成默认构造函数:

在这里插入图片描述

强制生成默认函数的关键字— delete

当我们在实现某些类的时候,如果不希望它被拷贝,通常有两种方式:

  1. 将其拷贝函数私有化:
#include<iostream>

using namespace std;


class A
{
public:
	A(int data) :
		data_(data)
	{

	}

	A() = default;
private:
	A(const A& a)
	{
		cout << "A(const A& a)" << endl;
	}

	A(A&& a)
	{
		cout << "A(A&& a)" << endl;
	}

	A& operator=(const A& a)
	{
		cout << "A& operator=(const A& a)" << endl;
		return *this;
	}

	A& operator=(A&& a)
	{
		cout << "A& operator=(A&& a)" << endl;
		return *this;
	}
	int data_;
};

int main()
{
	A a;
	A b(3);

	a = b;
	a = move(b);

	A tmp1(b);
	A tmp2(move(b));
	return 0;
}

在这里插入图片描述

2.C++11中更简单,直接将相应的默认函数删除。

#include<iostream>

using namespace std;


class A
{
public:
	A(int data) :
		data_(data)
	{

	}

	A() = default;
	A(const A& a) = delete;
	A& operator=(const A& A) = delete;
private:
	int data_;
};

int main()
{
	A a;
	A b(3);

	/*a = b;*/
	a = move(b);

	/*A tmp1(b);*/
	A tmp2(move(b));
	return 0;
}

同类型的左值和右值函数,我们删除一个就可以了,另外一个默认函数就不会生成了。

在这里插入图片描述

继承和多态中的final与override关键字

  1. final在继承中有两个作用:
    • 阻止基类被继承。
 class Base final {
public:
  void func() {
      // 实现  
  }
};

// 下面的代码会编译失败,因为Base是final的  
class Derived : public Base {};

int main()
{
   return 0;
}

在这里插入图片描述

  • 还有一个作用是C++14出现的,是阻止虚函数重写,这里我们不具体介绍了。

2.override

override明确表示一个子类重写了基类中的虚函数。

这样做有以下好处:

  • 编译器检查:如果你标记了一个函数为override,但基类中并没有一个同名的虚函数供其重写,编译器会报错。这有助于防止一些由于拼写错误或类型不匹配而导致的潜在问题。
  • 提高代码可读性:通过override关键字,其他开发者可以更容易地理解这个函数的意图和作用。
class Base {
public:
    virtual void func() {
        // 实现  
    }
};

class Derived : public Base {
public:
    void func() override {
        // 重写实现  
    }
};

基类如果没有对应虚函数就会报错(构成重写的要求见多态和继承部分博客)。

在这里插入图片描述

可变参数我们在C语言部分就已经接触过,它们我们再熟悉不过,就是scanfprintf函数:

在这里插入图片描述

这里我们的printf就是可变参数,我们可以给它传很多东西进去,要想把printf的原理完全弄清楚,需要学习系统部分的知识,在操作系统部分,我们会再谈到printf,这里就不详谈了。

在C++11前,我们的模板参数都是固定的个数,C++11出现了可变模板参数,这无疑是一个巨大的改进,但是可变模板参数比较抽象,使用起来没有那么容易:

可变参数模板的语法

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的args前面有省略号,所以它就是一个可变模板参数,我们把可变模板参数又叫做参数包。参数包里面有0~N个模板参数(N >= 0),我们无法直接获取参数包中的参数,这既是参数包的特点,也是难点,因为语法上不支持args[i]的形式,我们只能通过一些其它方法来获取参数包中的参数和值。

在这里插入图片描述

递归展开参数包

#include<iostream>
#include<vector>

using namespace std;

void Func_()//0个参数
{
	cout << "参数解析完毕" << endl;
}


template<class T, class ... Args>
void Func_(T& data, Args... args)
{
	cout << data << " data的类型为:" << typeid(data).name() << endl;
	Func_(args...);
}

template<class ...Args>
void Func(Args... args)
{
	Func_(args...);
}

int main()
{
	Func(0, 1, 2, "xxxxxx", 'c');
	return 0;
}

运行结果:

在这里插入图片描述

我们还写了一个递归终止函数,即参数为0时的情况。

可变参数模板的底层原理

所谓的可变参数模板,无非是函数模板实例化之后推演生成匹配的函数,如果有现成的就不需要实例化:

在这里插入图片描述

在这里插入图片描述

逗号表达式展开参数包

使用这种方式展开参数包,不再需要写递归返回函数。

#include<iostream>
#include<vector>
#include<string.>
using namespace std;

template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

运行结果:

在这里插入图片描述

我们对代码关键部分int arr[] = { (PrintArg(args), 0)... };做一下解释

  • 参数包展开:在函数模板ShowList中,Args... args是一个参数包,它可以接受任意数量和类型的参数。在 int arr[] = { (PrintArg(args), 0)... }; 中的使用是尝试对参数包中的每个元素进行展开,并对每个元素执行 (PrintArg(args), 0) 操作。
  • 逗号表达式:逗号运算符用于顺序执行两个表达式,并返回最后一个表达式的值。在我们的例子中,PrintArg(args), 0 会先调用 PrintArg 函数(该函数接受一个参数并打印它),然后表达式的结果为 0(因为逗号运算符返回其右侧表达式的值)。

但是由于我们是使用的初始化列表来初始化的数组,没有指定数组的大小,编译器只能通过初始化列表来推断数组的大小,但是这里初始化列表是通过模板参数包展开的,在很多编译器上无法推断数组大小,可能会报错。

#include<iostream>
#include<vector>
using namespace std;

template <class ...Args>
void ShowList(Args... args)
{
	vector<int> arr = {args... };

	for (auto num : arr)
		cout << num << " ";

	cout << "\nvector的大小:" << arr.size();
	cout << endl;
}

int main()
{
	ShowList(1, 2, 3, 4, 4, 5, 6, 7, 8, 9);
	return 0;
}

运行结果:

在这里插入图片描述
在我们的编译器上可以正常运行。(VS2019)

C++11中的emplace_back系列函数

这个函数也是用来插入元素的。

在这里插入图片描述
它支持模板的可变参数和万能引用,万能引用也是引用,可以减少拷贝,我们看它的底层肯定也会走完美转发:

在这里插入图片描述

那它和insert系列的函数有什么区别呢?

特征emplace_back insert(非emplace版本)emplaceinsert的一个版本)push_back
位置容器末尾指定位置指定位置容器末尾
元素构造直接在容器内存构造先构造再复制/移动直接在容器内存构造先在容器外部构造(可能需要复制或移动)
效率高效(避免复制/移动)可能较低(涉及复制/移动)高效(避免复制/移动)可能较低(涉及复制或移动)
灵活性较低(仅限于末尾)高(任意位置)高(任意位置)较低(仅限于末尾)
使用示例vec.emplace_back("Hello", 5, '!');vec.insert(vec.begin() + 1, "x");vec.emplace(vec.end(), "Hello", 5, '!');std::string temp = "Hello" + std::string(5, '!'); vec.push_back(temp);(先构造字符串,再复制或移动到容器)

我们用代码来验证一下上面的内容:

#include<iostream>
#include<list>
#include"string.h"

using namespace std;
int main()
{
	list<my_string::string> l1;
	l1.emplace_back("xxxxxxxxx");
	cout << "------------------------------------" << endl;

	l1.push_back("xxxxxxxxxxxx");
	return 0;
}

运行结果:

在这里插入图片描述
emplace_back减少了一次移动构造,因为它是直接在容器内存构造。emplace_back可以在内存中直接构造所依赖的两个条件:

  • 定位new:允许我们对已经分配内存,但还没有创建对象的内存初始化并创建对象,也就是显式的调用构造函数。
  • 参数包,允许我们传多个不同类型的值来作为定位new表达式的参数(构造函数的参数)。push_back不行,只能传T作为参数,T是容器里元素的类型,但是这个元素可能是自定义类型,它里面还有其它的成员。

在这里插入图片描述

我们通过阅读源码,发现vs2019的库里面,emplace_back确实使用到了定位new。

在这里插入图片描述
因为它使用到了定位new,所以我们猜测,vectorerase系列的方法里面,并不直接释放对象的空间,而是要显式的去释放该对象的资源(去调用T的析构函数):

在这里插入图片描述

和我们想的一致,这样那片空间又变成没有创建对象的空间了。

emplace_back与push_back效率比较

  1. 插入内置类型。
#include<iostream>
#include<list>
#include<vector>
#include"string.h"
#include<time.h>
using namespace std;

void test1()
{
	vector<int> v1;
	const int N = 100000000;

	size_t begin1 = clock();
	for (int i = 0; i < N; ++i)
	{
		v1.push_back(i);
	}

	size_t end1 = clock();

	vector<int> v2;
	size_t begin2 = clock();
	for (int i = 0; i < N; ++i)
	{
		v2.emplace_back(i);
	}

	size_t end2 = clock();

	cout << "push_back: " << end1 - begin1 << endl;
	cout << "emplace_back: " << end2 - begin2 << endl;
}
int main()
{
	test1();
	return 0;
}

运行结果:

在这里插入图片描述

2.插入浅拷贝的自定义类型对象(直接给插入对象参数的情况下)。
emplace_back可以减少一次拷贝。

  • 注意:由于emplace_back是可变模板参数的形式,它不支持{}初始化列表对象作为它的参数,这和它的设计初衷是有关系的(可变模板参数(如模板函数中的…)主要是为了提供一种灵活的方式来处理未知数量和类型的参数。它们允许开发者编写能够接受任意数量和类型参数的模板代码,但并未直接关联到特定的数据结构或初始化方式,如初始化列表。)

在这里插入图片描述

#include<list>
#include<iostream>

using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_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 = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	std::list<Date> lt1;
	lt1.push_back({ 2024,3,30 });
	cout << "----------------------------" << endl;
	// 不支持
	//lt1.emplace_back({2024,3,30 });

	// 推荐
	lt1.emplace_back(2024, 3, 30);
	return 0;
}

运行结果:

在这里插入图片描述
测试效率:

#include<list>
#include<iostream>
#include<time.h>

using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		:_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 = 1;
	int _month = 1;
	int _day = 1;
};


void test2()
{
	const int N = 10000000;

	list<Date> l1;

	size_t begin1 = clock();
	for (int i = 0; i < N; ++i)
	{
		l1.push_back({ rand() + i,rand() + i,rand() + i });
	}
	size_t end1 = clock();

	list<Date> l2;
	size_t begin2 = clock();

	for (int i = 0; i < N; ++i)
	{
		l2.emplace_back(rand() + i, rand() + i, rand() + i);
	}
	size_t end2 = clock();

	cout << "push_back效率:" << end1 - begin1 << endl;

	cout << "emplace_back效率:" << end2 - begin2 << endl;

}
int main()
{
	srand(time(NULL));
	test2();
	return 0;
}

运行结果:

在这里插入图片描述
emplace_back也没有快多少。

3.插入深拷贝的自定义类型对象(有移动构造和移动赋值重载函数)。

#include<list>
#include<iostream>
#include<time.h>
#include"string.h"
using namespace std;



void test2()
{
	const int N = 10000000;

	list<my_string::string> l1;

	size_t begin1 = clock();
	for (int i = 0; i < N; ++i)
	{
		l1.push_back("XXXXXXXXXXXXXXXXXX");
	}
	size_t end1 = clock();

	list<my_string::string> l2;
	size_t begin2 = clock();
	//cout << "----------------------" << endl;
	for (int i = 0; i < N; ++i)
	{
		l2.emplace_back("XXXXXXXXXXXXXXXXXXXXXXX");
	}
	size_t end2 = clock();

	cout << "push_back效率:" << end1 - begin1 << endl;

	cout << "emplace_back效率:" << end2 - begin2 << endl;

}
int main()
{
	srand(time(NULL));
	test2();
	return 0;
}

运行结果:

在这里插入图片描述

push_back每次只用多调用一次移动构造。

  1. 深拷贝的自定义类型作为其成员但是没有实现移动拷贝和移动赋值重载。
    还是刚刚的代码,我们把我们自己实现的string的移动构造给注释。

在这里插入图片描述
此时的区别就是push_backemplace_back多了一次拷贝构造(深拷贝),但是emplace_back不需要,因为它是使用const char*直接在内存里初始化它的元素,调用对应的构造函数。

在这里插入图片描述

此时push_back慢了1秒,计算机的1s可以计算很多东西,可以看出移动构造还是很重要的,在需要深拷贝的类中,我们尽量要实现移动构造和移动赋值。

总结:emplace_back这种插入的效率很客观,我们可以多使用它。尤其是一些没有实现深拷贝的类,使用emplace_back(给插入对象参数情况下),可以减少一次深拷贝。

  • 本人知识、能力有限,若有错漏,烦请指正,非常非常感谢!!!
  • 转发或者引用需标明来源。

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

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

相关文章

C#使用log4net结合sqlite数据库记录日志

0 前言 为什么要把日志存到数据库里? 因为结构化的数据库存储的日志信息,可以写专门的软件读取历史日志信息,通过各种条件筛选,可操作性极大增强,有这方面需求的开发人员可以考虑。 为什么选择SQLite? 轻量级数据库,免安装,数据库的常用的基本功能都有,可以随程序…

批量修改YOLO格式的标注类别

1.解决的问题 假如你有一个YOLO格式的数据集&#xff0c;标注类别为0&#xff0c;1&#xff0c;2&#xff0c;3四个类别标签。如果你想删除标签1&#xff0c;只保留0&#xff0c;2&#xff0c;3类别的标注信息&#xff0c;或者想将标签0和标签1合并为标签1&#xff0c;只剩下标…

第三届“奇安信杯”网络安全技能竞赛 部分题目WriteUP

第三届“奇安信杯”网络安全技能竞赛WriteUP 文章目录 第三届“奇安信杯”网络安全技能竞赛WriteUPMISCGIFpycseeyouagain CRYPTObase全家桶base6432rsa WEB MISC GIF 下载附件&#xff0c;解压得到test1.jpg。 用010 Editor打开&#xff0c;发现GIF文件头&#xff0c;修改文…

从“Hello World”到“Success” —— 1024程序员节的感悟与成长

目录 1.成为程序员 2.成长之路 3.困难与挑战 4.磨炼与前进 5.总结与收获 6.感悟 1.成为程序员 今天&#xff0c;我们迎来了专属于程序员的节日——1024程序员节。这一天不仅是对所有编程爱好者的致敬&#xff0c;更是回顾过去一年来成长历程的时刻。对于每一位踏上编程之旅…

AI带货主播如何打造真实视觉效果!

AI带货主播作为新兴的数字营销手段&#xff0c;正在逐步改变着电商行业的面貌&#xff0c;AI技术的不断进步使得带货主播能够以更加真实、生动的视觉效果展现在消费者面前&#xff0c;从而大大提升了购物体验和销售转化率。 那么&#xff0c;AI带货主播如何打造真实视觉效果呢…

深入浅出神经网络:从基础原理到高级应用

第5章 神经网络 更加详细内容可以看这篇文章 5.1 神经元模型 神经网络的基本单元是神经元模型。神经元模拟了生物神经元的行为&#xff0c;通过接收输入信号&#xff0c;进行加权求和&#xff0c;然后经过激活函数输出结果。 数学上&#xff0c;一个简单的神经元可以表示为&…

业务开发常见问题-并发工具类

hello&#xff0c;大家好&#xff0c;本讲我们一起聊一下常见的几个并发工具类的使用和坑&#xff01; 在日常工作中&#xff0c;我们经常会遇到多线程并发问题&#xff0c;比如ThreadLocal、锁、ConcurrentHashMap、CopyOnWriteArrayList等。那么如何正常的使用呢&#xff1f;…

P7400 [COCI2020-2021#5] Magenta 题解

#1024程序员节&#xff5c;征文# 人生中的第二道紫题。。。 题目传送门 解题思路 下文中的距离指的是 a , b a,b a,b 之间的边的数量。 Sub 2 即所有边 Paula 与 Marin 都可以行走。 根据题意 Paula 先手。因此&#xff0c;如果一开始 Paula 动不了&#xff0c;那么 M…

浏览器的渲染过程

文章目录 什么是浏览器的渲染&#xff1f;浏览器渲染过程面试问点&#xff1a;为什么操作DOM慢&#xff1f;回流与重绘那么&#xff0c;什么情况下会触发回流&#xff1f; 浏览器的优化 什么是浏览器的渲染&#xff1f; 简单的说就是浏览器将 HTML 代码解析出来&#xff0c;把…

轻松学会!回收站数据恢复的几种妙招

回收站数据恢复方法是一个涉及计算机操作和数据安全的重要话题。在日常使用电脑的过程中&#xff0c;我们经常会遇到误删文件或清空回收站的情况&#xff0c;导致重要数据丢失。幸运的是&#xff0c;有多种方法可以尝试恢复这些丢失的数据。以下将详细介绍几种常见的回收站数据…

C++: C/C++内存管理

前言 本篇博客将详细介绍C的内存管理 &#x1f496; 个人主页&#xff1a;熬夜写代码的小蔡 &#x1f5a5; 文章专栏&#xff1a;C 若有问题 评论区见 &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 ​ 一.C/C内存分布 让我们先来看看下面的代码吧 int globalVar 1; st…

【植物识别系统】Python+人工智能+深度学习+卷积神经网络算法+TensorFlow+算法模型+Django网页界面平台

一、介绍 植物识别系统&#xff0c;使用Python作为主要编程语言开发&#xff0c;通过收集常见的6中植物树叶&#xff08;‘广玉兰’, ‘杜鹃’, ‘梧桐’, ‘樟叶’, ‘芭蕉’, ‘银杏’&#xff09;图片作为数据集&#xff0c;然后使用TensorFlow搭建ResNet50算法网络模型&am…

C++:模板的特化与分离编译

之前我们在介绍模板的时候仅仅是简单的介绍了模板的用法&#xff0c;本篇文章我们来详细的介绍下模板中比较重要的几个点。 一&#xff0c;非类型模板参数 我们之前的c中&#xff0c;会将经常使用的而又确保在我们程序的运行过程中值不会改变的值进行#define&#xff1a; #d…

Unity Apple Vision Pro 保姆级开发教程-环境配置、导入 PolySpatial 案例、程序发布到设备

视频教程 Unity 环境配置、导入 PolySpatial 案例、程序发布到设备 Unity Vision Pro 中文课堂教程地址&#xff1a; Unity3D Vision Pro 开发教程【保姆级】 | Unity 中文课堂 教程说明 这期教程我将介绍使用 Unity 开发 Apple Vision Pro 应用所需要的 Unity 环境配置&…

055_基于python摄影平台交流系统

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

Android compose 重建流程1

前言 本文是笔者学习Compose是如何自动触发UI刷新的笔记,可能缺乏一定可读性和教导性.(建议阅读参考文献更具启发性) 使用以下BOM作为研究环境. composeBom "2024.04.01" androidx-compose-bom { group "androidx.compose", name "compose-bom…

实习冲刺Day2

算法题 反转链表 206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}*…

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;如下图所示&#xff09;检索增强生成&#xff0c;即大模型LLM在回答问题时&#xff0c;会先从大量的文档中检索出相关信息&#xff0c;然后基于这些检索出…

【LLaMA-Facrory】【模型评估】:代码能力评估——Qwen-Coder-7B 和 deepseek-coder-7b-base-v1.5

目录 序言 1 拉取 Qwen2.5-Coder-7B 模型 2 编写python测试模型 3 启动webui导入模型测试 4 模型评估 4.1 前期准备工作 4.2 Qwen2.5-Coder-7B 模型评估 数据说明 综合分析 4.3 deepseek-coder-7b-base-v1.5 模型评估 数据说明 综合分析 4.4 模型比较 1. 文本生成…

软件安全测试报告如何编写?CMA、CNAS软件安全测试机构推荐

随着软件产品的增多&#xff0c;产品安全成为软件企业留住用户的方法之一。安全测试是验证和检查软件安全的重要手段&#xff0c;而软件安全测试报告则是测试人员工作成果的最好体现&#xff0c;那么软件安全测试报告该如何编写呢?权威的CMA、CNAS软件安全测试机构又有哪些? …