【C++深度探索】全面解析多态性机制(二)

news2024/9/25 7:26:22

在这里插入图片描述

🔥 个人主页:大耳朵土土垚
🔥 所属专栏:C++从入门至进阶
这里将会不定期更新有关C/C++的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

在这里插入图片描述

前言

我们知道C++多态实现有两个条件——一是基类的指针或引用调用虚函数,另一个是基类中有虚函数并且在派生类中实现虚函数重写;这两个条件缺一不可,这与多态实现的底层原理有关,今天我们就来学习一下多态实现的原理🥳🥳

目录

  • 前言
  • 1.虚函数表
  • 2.派生类中的虚表
    • 情况一:只有基类有虚函数,派生类没有
    • 情况二:基类和派生类中都有虚函数,并且虚函数没有被重写
    • 情况三:基类中定义虚函数,并且派生类中对该虚函数进行了重写
    • 综合这三种情况
  • 3.多态原理
    • 动态绑定与静态绑定
  • 4.多继承中的虚函数表
  • 5.结语

1.虚函数表

虚函数表(Virtual Function Table,VTable)是C++中实现动态多态性的一种机制。每个包含虚函数的类都有一个对应的虚函数表,用于存储该类的虚函数地址。

虚函数表是一个包含函数指针的数组,每个函数指针指向相应虚函数的实现。

也就是说在类中定义了虚函数,那么该类就会包含一个虚函数表来存放虚函数的函数指针,注意这里是指类中会存储虚函数表的指针来达到效果,因为如果虚函数很多,直接存储虚函数表可能会占用很多空间。

例如:

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

int main()
{
	cout << sizeof(Base) << endl;
	return 0;
}

当我们计算Base类的大小时,发现只有一个int类型的成员变量_b,所以应该是4个字节,但是我们可以看一下结果:

在这里插入图片描述

这里显示的是8字节,这是因为Base类中创建了虚函数,而每个包含虚函数的类都有一个对应的虚函数表,虚函数的地址要被放到虚函数表中,所以需要多余的空间来存储虚函数表的指针,这个指针我们叫做虚函数表指针。

如下图所示:

在这里插入图片描述

Base对象b中除了_b成员,还多一个__vfptr指针放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。

注意是虚函数表的指针,而不是直接存储虚函数表。

因为上述例子是在32位操作系统下执行的,所以指针的大小是4字节,Base类大小是8字节;如果是64位那么指针的大小是8字节,Base类的大小就应该参考结构体内存对齐规则,应该是16字节。

2.派生类中的虚表

如果基类中没有虚函数,派生类中有虚函数,那么它的虚函数表和上面的一致。
例如:

class Base
{
private:
	int _b = 1;
};

class Derive :public Base
{
public:
	virtual void Func2()
	{
		cout << "Func2()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Derive d;
	return 0;
}

上述代码基类中没有虚函数也就不存在虚函数表,但派生类Derive类中存在虚函数,所以会存放一个虚函数表指针__vfptr,来指向虚函数表,而虚函数表中又会存放Derive类中虚函数的指针。

如下图所示:

在这里插入图片描述

但是如果基类中有虚函数表,那么派生类该如何继承呢?

情况一:只有基类有虚函数,派生类没有

例如:

//情况一:只有基类有虚函数
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

class Derive :public Base
{
private:
	int _d = 2;
};

int main()
{
	Derive d;
	return 0;
}

d对象由两部分构成,一部分是父类继承下来的成员,另一部分是自己的成员,如下图所示:

在这里插入图片描述

我们看到d对象继承了基类的成员变量_b和虚函数表的指针,虚函数表里面存放的是基类中虚函数Func1()的地址。

情况二:基类和派生类中都有虚函数,并且虚函数没有被重写

例如:

//情况二:基类和派生类中都有虚函数,并且虚函数没有被重写
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

class Derive :public Base
{
public:
	virtual void Func2()
	{
		cout << "Func2()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Derive d;
	return 0;
}

首先基类中有虚函数,所以派生类中包含基类的那部分成员肯定会包含基函数表的指针,但是派生类的虚函数应该怎么存放呢?结果如下图所示:

在这里插入图片描述

我们发现派生类并没有生成自己的虚函数表,所以它的虚函数应该存放在从基类继承下来的虚函数表中,上图中看到虚函数数组内只存放一个虚函数Func1(),没有派生类自己的虚函数Fun2(),这里是编译器的监视窗口故意隐藏了Fun2()函数,也可以认为是他的一个小bug,那么我们可以思考从打印虚函数地址的思路入手,查看Fun1()Fun2()的地址是否是存放在一起。

虚函数的地址存放在虚函数表中,而对象中前四个字节存放的是虚函数表的指针,所以我们可以使用强制类型转换取出对象的前四个字节,但是int类型与BaseDerive类型不兼容,不能相互转换,但是指针之间可以相互转换,所以我们考虑先取BaseDerive类对象的地址然后强制转换成int*类型,然后再解引用就得到了虚函数表的地址🥳🥳
代码如下:

//情况二:基类和派生类中都有虚函数,并且虚函数没有被重写
//基类和派生类代码如上

//先定义一个函数指针类型
typedef void(*VFPTR) ();

//打印函数指针数组中存放的函数地址
void PrintVTable(VFPTR vTable[])
{
	// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; i++)
	{
		printf(" 第%d个虚函数地址 :0X%x->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();//调用该函数
	}
	cout << endl;
}

void test()
{
	Base b;
	Derive d;
	//打印b对象中虚函数的地址
	VFPTR* vTableb = (VFPTR*)(*(int*)&b);
	PrintVTable(vTableb);
	
	//打印d对象中虚函数的地址
	VFPTR* vTabled = (VFPTR*)(*(int*)&d);
	PrintVTable(vTabled);
}

结果如下:

在这里插入图片描述

我们发现基类对象和派生类对象的虚函数表是不同的,并且派生类对象虚函数表中存放了两个虚函数的地址,其中一个与基类的虚函数地址相同也就是Func1()的地址,另一个则是派生类自己定义的虚函数Func2()的地址。

综上所述,如果派生类和基类都定义了自己的虚函数,并且基类的虚函数没有在派生类中重写的话,那么派生类中虚函数的地址会存放在派生类继承的基类那部分的虚函数表中的末尾,并且基类定义的对象和派生类定义的对象的虚函数表的地址是不同的。

同一类定义的不同对象使用的基函数表是同一个
如下图所示:

在这里插入图片描述

情况三:基类中定义虚函数,并且派生类中对该虚函数进行了重写

例如:

//情况三:基类中定义虚函数,并且派生类中对该虚函数进行了重写
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
private:
	int _b = 1;
};

class Derive :public Base
{
public:
	virtual void Func1()//重写虚函数Fun1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
	return 0;
}

按照之前的结论,派生类中的虚函数的地址是存放在继承的基类的虚函数表中的,那么对于重写的虚函数是写在基类虚函数表的末尾,还是将基类被重写的虚函数地址覆盖呢?

结果如下图:

在这里插入图片描述

可以看到派生类的重写的虚函数的地址覆盖了继承的基类的虚函数的地址,我们还可以使用上文中打印虚函数地址的方式更加直观的看清楚:

在这里插入图片描述

上述例子中基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。

综合这三种情况

  1. 如果基类没有虚函数,子类有虚函数,那么子类会自己生成一个虚函数表来存放自己的虚函数;
  2. 如果基类有虚函数,子类也有自己的虚函数,那么子类中虚函数的地址会存放在子类继承的基类那部分的虚函数表中的末尾;

注意虽然说是继承基类的虚函数表,但是基类对象和子类对象的虚函数表是不同的表,它们各自有各自的表。只是因为子类会继承基类的虚函数,所以基类的虚函数指针也会存在该子类的虚函数表中,相当于将基类的虚函数表直接继承下来,再将子类自己的虚函数指针存放进去,子类也就不用自己再生成一个虚函数表。

  1. 如果基类有虚函数,并且子类对该虚函数进行了重写,那么子类虚函数表中基类被重写的虚函数地址就会被子类重写的虚函数地址覆盖,而不再和第二点一样写在虚函数表的尾部。

例如:

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};

class Derive : public Base
{
public:
	virtual void Func1()//重写
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
}

在这里插入图片描述

基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会放进虚表。

虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr

总结一下派生类的虚表生成:
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

✨✨这里还有一个很容易混淆的问题:虚函数存在哪的?虚表存在哪的?

虚函数存在虚表,虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。那么虚表存在哪的呢?实际我们去验证一下会发现vs下是存在代码段的。

代码如下:

int main()
{
	int i = 0;
	static int j = 1;
	int* p1 = new int;
	const char* p2 = "aaaaa";
	Base b;
	Derive d;
	printf("栈:%p\n", &i);
	printf("静态区:%p\n", &j);
	printf("堆:%p\n", p1);
	printf("常量区:%p\n", p2);
	printf("Base虚表地址:%p\n", *(int*)&b);
	printf("Derive虚表地址:%p\n", *(int*)&d);

	return 0;
}

通过比较不同区域的地址来判断虚表地址在哪,发现虚表离常量区最近,如下图所示:

在这里插入图片描述

3.多态原理

了解了虚函数表我们就可以深入学习多态的原理。

例如:

//多态原理
class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};


void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person Black;
	Func(Black);

	Student tutu;
	Func(tutu);
	return 0;
}

对于买票这个行为,不同的对象会有不同的结果,普通人买票是全价,学生则可能是半价,如下图所示:

在这里插入图片描述

那么我们可以通过调试来看看它有不同结果的原因:

当使用Person类对象调用函数Func时:

在这里插入图片描述

当使用Student类对象调用函数Func时:

在这里插入图片描述

我们看到,p是指向Black对象时,p->BuyTicket在Black的虚表中找到虚函数是Person::BuyTicket。p是指向tutu对象时,p->BuyTicket在tutu的虚表中找到虚函数Student::BuyTicket。

我们发现不同的对象调用Func函数时,使用的虚函数表是不同的,Person类对象和Student类对象都使用各自的虚函数表,所以调用不同的虚函数,如下图所示:

在这里插入图片描述

这样就实现出了不同对象去完成同一行为时,展现出不同的形态。

反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是基类对象的指针或引用调用虚函数。具体原因,我们先要了解一下动态绑定。

动态绑定与静态绑定

  • 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载
  • 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

那么当我们直接使用对象调用成员函数时走的是静态绑定,是指编译期间就确定的程序行为;当我们使用基类指针或引用调用虚函数时走的是动态绑定,需要通过虚函数表来确定不同对象调用不同的函数,根据具体拿到的类型确定程序的具体行为。

所以对于多态实现的两个条件,首先我们需要通过基类对象的指针或引用调用虚函数才能走动态绑定,其次派生类的虚函数还需要重写,这样不同类的对象使用的虚函数才是不一样的,才会显现不同的状态,实现多态。

如果只是完成了虚函数的覆盖而没有通过基类对象的指针或引用调用,或者只有第二个条件都无法完成多态的实现。

多态实现的两个条件缺一不可。

4.多继承中的虚函数表

在多继承中,派生类会继承多个基类,每个基类都有自己的虚表。因此,派生类会有多个虚表,每个虚表对应于一个基类。

这是为了保证派生类能够正确调用和重写每个基类的虚函数。当派生类实例化时,会为每个基类分配一个虚表指针,这些虚表指针会存储在派生类对象的内存中。

例如:

class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};

class Base2 {
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};

class Derive : public Base1, public Base2 {
public:
	virtual void func1() { cout << "Derive::func1" << endl; }//重写Fun1()
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};

//打印虚表地址以及虚函数地址
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl;
}

int main()
{
	Derive d;
	//第一个虚表地址
	VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
	PrintVTable(vTableb1);
	//找到第二个虚表的地址
	VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
	PrintVTable(vTableb2);
	return 0;
}

结果如下:

在这里插入图片描述

上图可以看出多继承派生类的未重写的虚函数func3()放在第一个继承基类部分的虚函数表中,图示如下:

在这里插入图片描述

5.结语

虚函数表的存在是为了实现动态绑定也就是实现多态,当派生类对基类的虚函数进行重写时,通过基类对象指针和引用调用虚函数时,就会通过虚函数表来确定不同对象调用不同的函数,根据具体拿到的类型确定程序的具体行为,所以多态实现的两个条件缺一不可。以上就是今天所有的内容啦~ 完结撒花🥳🎉🎉

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

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

相关文章

web零碎知识

在后端想要发送一个JSON对象可以使用Gson // 使用 Gson 将对象转换为 JSON 字符串Gson gson new Gson();String json gson.toJson(user); 就会把一个对象转化为一个JSON字符串&#xff0c; // 设置响应内容类型为 JSONresponse.setContentType("application/json&quo…

卷积神经网络-猫狗识别实战

课程来自bilibiliMomodel平台 全长只有两个小时&#xff0c;理论部分讲得很粗糙 1 人的视觉和计算机视觉 人的大脑&#xff1a;神经元细胞&#xff0c;轴突发送信号&#xff0c;树突接收信号&#xff0c;互相连接&#xff0c;连接的强度和状态会随着新的经历刺激而变化。 用…

尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程

Vue3 编码规范 创建vue3工程 基于vite创建 快速上手 | Vue.js (vuejs.org) npm create vuelatest 在nodejs环境下运行进行创建 按提示进行创建 用vscode打开项目 安装依赖 源文件有src 内有main.ts App.vue 简单分析 编写src vue2语法在三中适用 vue2中的date metho…

C++基础知识:冒泡排序(利用C++实现冒泡排序)

1.冒泡排序的作用: 最常用也是简单的排序算法&#xff0c;对数组内元素进行排序 2.冒泡排序的具体步骤&#xff1a; 1.比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 2.对每一对相邻元素做同样的工作&#xff0c;执行完毕后&#xff0c;找到第一个最大值…

关于思维和智能体模型的思考(2)

在关于思维和智能体模型的思考&#xff08;1&#xff09;一文中&#xff0c;我们提出了思维和Agent 模型&#xff0c;提出了使用确定连接的智能体构建的思维模型。本文我们继续讨论思维与智能体&#xff0c;重点探讨另一种智能体-自主智能体&#xff0c;并且提出了自主智能体的…

《黑马点评》Redis高并发项目实战笔记【完结】P1~P72

花费4周敲完《黑马点评》的课程&#xff0c;做了详细的笔记&#xff0c;感觉受益匪浅&#xff0c;一直一直都在不停成长着。 突然想起《苍穹外卖》系列至今已收获200个赞&#xff0c;500个收藏&#xff0c;好评颇多&#xff0c;私信我的人不计其数&#xff0c;在此谢谢大家。 …

从零开始学习PX4源码3(如何上传官网源码到自己的仓库中)

目录 文章目录 目录摘要1.将PX4源码上传至腾讯工蜂2.从腾讯工蜂克隆源码到本地ubuntu3.如何查看自己源码的版本信息 摘要 本节主要记录从零开始学习PX4源码3(如何上传官网源码到自己的仓库中)及如何查看PX4的固件版本信息&#xff0c;欢迎批评指正&#xff01; PX4源码版本V1.…

东软“引战”国家队 通用技术“补链”大国重器

向来低调温和的东软创始人刘积仁&#xff0c;这一次抛出了“王炸”级的资产交易。 7月3日&#xff0c;《多肽链》获得一则足以引爆国内医疗设备行业的投资信息&#xff1a;被东软集团视为核心资产、掌上明珠的东软医疗&#xff0c;成功引入通用技术集团资本有限公司与中国国有…

240713_昇思学习打卡-Day25-LSTM+CRF序列标注(4)

240713_昇思学习打卡-Day25-LSTMCRF序列标注&#xff08;4&#xff09; 最后一天咯&#xff0c;做第四部分。 BiLSTMCRF模型 在实现CRF后&#xff0c;我们设计一个双向LSTMCRF的模型来进行命名实体识别任务的训练。模型结构如下&#xff1a; nn.Embedding -> nn.LSTM -&…

前端练习小项目——方向感应名片

前言&#xff1a;在学习完HTML和CSS之后&#xff0c;我们就可以开始做一些小项目了&#xff0c;本篇文章所讲的小项目为——方向感应名片 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 在开始学习之前&#xff0c;先让我们看一…

C++客户端Qt开发——开发环境

一、QT开发环境 1.安装三个部分 ①C编译器&#xff08;gcc&#xff0c;cl.exe……) ②QT SDK SDK-->软件开发工具包 比如&#xff0c;windows版本QT SDK里已经内置了C的编译器&#xff08;内置编译器是mingw&#xff0c;windows版本的gcc/g&#xff09; ③QT的集成开发…

KnoBo:医书学习知识,辅助图像分析,解决分布外性能下降和可解释性问题

KnoBo&#xff1a;从医书中学习知识&#xff0c;辅助图像分析&#xff0c;解决分布外性能下降问题 提出背景KnoBo 流程图KnoBo 详解问题构成结构先验瓶颈预测器参数先验 解法拆解逻辑链对比 CLIP、Med-CLIPCLIPMed-CLIPKnoBo 训练细节预训练过程OpenCLIP的微调 构建医学语料库文…

说说执行一条查询SQL语句时,期间发生了什么?

执行一条查询SQL语句时&#xff0c;期间发生了什么&#xff1f; 前言说说执行一条查询SQL语句时&#xff0c;发生了什么&#xff1f;连接器权限验证断开连接长连接 查询缓存查询缓存的问题 解析器词法分析语法分析 执行 SQL预处理器优化器执行器主键索引查询全表扫描索引下推 总…

轻薄鼠标的硬核选购攻略,很多人都在“高性价比”鼠标上栽跟头了

轻薄款设计的鼠标是目前鼠标市场的出货大头&#xff0c; 也是价格最卷的一类鼠标。 比游戏鼠标或许更卷一些。 这和当前的移动办公趋势关系很大。 这类鼠标主要跟笔记本和iPad搭配。 核心的使用场景是办公。 因此轻薄和静音是这类鼠标的核心卖点。 同时用户并不愿意付出太…

代码随想录算法训练营第三十二天|1049.最后一块石头的重量II、494.目标和、474.一和零

1049.最后一块石头的重量II 有一堆石头&#xff0c;每块石头的重量都是正整数。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果如下&#xff1a; 如果 x y&#xff0c;那…

期货交易记录20240713

文章目录 期货交易系统构建步骤一、选品二、心态历练三、何时开仓3.1、开仓纪律3.2、开仓时机3.3、开仓小技巧 四、持仓纪律五、接下来的计划 2024年7月13号&#xff0c;期货交易第5篇记录。 交易记录&#xff1a;半个月多没记录了&#xff0c;这段时间分别尝试做了菜粕、棕榈油…

9.6 栅格图层符号化唯一值着色渲染

文章目录 前言多波段彩色渲染唯一值着色QGis设置为唯一值着色二次开发代码实现唯一值着色 总结 前言 介绍栅格图层数据渲染之唯一值着色渲染说明&#xff1a;文章中的示例代码均来自开源项目qgis_cpp_api_apps 多波段彩色渲染唯一值着色 以“with_color_table.tif”数据为例…

【嵌入式DIY实例-ESP8266篇】-LCD ST7789显示DS1307 RTC时间数据

LCD ST7789显示DS1307 RTC时间数据 文章目录 LCD ST7789显示DS1307 RTC时间数据1、硬件准备与接线2、代码实现本文将介绍如何使用 ESP8266 NodeMCU 板和 DS1307 RTC 集成电路构建简单的实时时钟和日历 (RTCC),其中时间和日期打印在 ST7789 TFT 显示模块上。 ST7789 TFT 模块包…

Open-TeleVision——通过VR沉浸式感受人形机器人视野:兼备远程控制和深度感知能力

前言 7.3日&#xff0c;我司七月在线(集AI大模型职教、应用开发、机器人解决方案为一体的科技公司)的「大模型机器人(具身智能)线下营」群里的一学员发了《Open-TeleVision: Teleoperation with Immersive Active Visual Feedback》这篇论文的链接&#xff0c;我当时快速看了一…

UML/SysML建模工具更新情况(2024年7月)(1)

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 工具最新版本&#xff1a;Enterprise Architect 17.0 BETA 更新时间&#xff1a;2024年7月2日 工具简介 性价比很高&#xff0c;目前最流行的UML建模工具。还包含需求管理、项目估算…