【C++】拆分详解 - 模板

news2024/10/24 2:20:46

文章目录

  • 一、泛型编程
  • 二、函数模板
    • 1. 概念
    • 2. 语法
    • 3. 函数模板的原理
    • 4. 函数模板的实例化
    • 5. 模板参数的匹配原则
  • 三、类模板
    • 1. 语法
    • 2. 实例化
  • 四、模板的特化
    • 1. 概念
    • 2. 函数模板特化
    • 3. 类模板特化
      • 3.1 全特化
      • 3.2 偏特化 / 半特化
      • 3.3 应用示例
    • 4. 小结
  • 五、模板的分离编译
      • 1. 分离编译的概念
      • 2. 模板不能分离编译
  • 六、模板总结
  • 总结

一、泛型编程

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}
  • 可以看到,为了实现一个尽可能通用的交换函数,我们需要进行多次函数重载,但是有缺陷

    1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增

      加对应的函数

    2. 代码的可维护性比较低,一个出错可能所有的重载均出错

  • 于是模板就被发明出来了,我们只需要造一个模具,编译器就能根据不同的类型生成不同的代码(本质上就是函数重载的活交给编译器干了)

    泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

    在这里插入图片描述


二、函数模板

1. 概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本

2. 语法

  • template<typename T1, typename T2,…,typename Tn>
  • 注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
template<typename T>
void Swap( T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

3. 函数模板的原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用
在这里插入图片描述

4. 函数模板的实例化

  • 用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

    1. 隐式实例化:让编译器根据实参推演模板参数的实际类型

      template<class T>
      T Add(const T& left, const T& right)
      {
      	return left + right;
      }
      
      int main()
      {
      	int a1 = 10, a2 = 20;
      	Add(a1, a2);
      	return 0;
      }
      
      • 模板参数个数 与 传参类型个数 不一致

        int main()
        {
        	int a3 = 30;
        	double d1 = 10.0;
        	Add(a3, d1);
        	return 0;
        }
        /*
        	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
        	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
        	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
        	注意:在模板中,编译器一般不会进行类型转换操作,
        			  因为一旦转化出问题,编译器就需要背黑锅
        */
        

        解决:

        1. 用户自己来强制转化
          Add(a3, (int)d1); 如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错
        2. 使用显式实例化
    2. 显式实例化:在函数名后的<>中指定模板参数的实际类型

      int main()
      {
      	int a = 10;
      	int b = 20;
      	// 显式实例化
      	Add<int>(a, b); 
      	return 0;
      }
      
      int main()
      {
      	int a3 = 30;
      	double d1 = 10.0;
      	// 显式实例化
      	Add<int>(a3, d1); //隐式类型转换
      	return 0;
      }
      

5. 模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

    思考一下,会不会发生重定义问题?答案是不会,详细解释请跳转到 “本文五、2.2 解决方法处”

    // 专门处理int的加法函数(现成的)
    int Add(int left, int right)
    {
    	return left + right;
    }
    
    // 通用加法函数(模板)
    template<class T>
    T Add(T left, T right)
    {
    	return left + right;
    }
    
    void Test()
    {
    	Add(1, 2); // 调用现成的
    	Add<int>(1, 2); // 显式实例化,调用编译器特化的Add版本(根据现成的,用模板生成一份相同的)
    }
    
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。(人话:有现成用现成的,没有就再造一个)

    • 如何界定到底是匹配模板还是匹配现成的?

      这里涉及到参数匹配的优先级: 完全匹配 > 模板替换后匹配 > 隐式类型转换后匹配

    // 专门处理int的加法函数
    int Add(int left, int right)
    {
    	return left + right;
    }
    // 通用加法函数
    template<class T1, class T2>
    T1 Add(T1 left, T2 right)
    {
    	return left + right;
    }
    
    void Test()
    {
    	Add(1, 2); // 调用现成的
    	Add(1, 2.0); // 模板函数可以生成更加匹配的版本,匹配优先级:
    				// 函数模板 > 隐式类型转换(现成的)
    }
    

三、类模板

1. 语法

template<class T1, class T2, ..., class Tn>
class 类模板名
{
	// 类内成员定义
};
#include<iostream>
using namespace std;
// 类模版
template<typename T>
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		_array = new T[capacity];
		_capacity = capacity;
		_size = 0;
	}
	void Push(const T& data);
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};

// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误,具体原因后面会讲
template<class T>
void Stack<T>::Push(const T& data)
{
	// 扩容
	_array[_size] = data;
	++_size;
}

int main()
{
	Stack<int> st1; // int
	Stack<double> st2; // double
	return 0;
}

2. 实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可

// Stack是类名,Stack<int>才是类型
Stack<int> st1; // int
Stack<double> st2; // double

四、模板的特化

1. 概念

在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

2. 函数模板特化

  • 语法:

    1. 关键字template后面接一对空的尖括号<>
    2. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
    // 函数模板 -- 参数匹配
    template<class T>
    bool Less(T left, T right)
    {
    	return left < right;
    }
    
    // 对Less函数模板进行特化
    template<>
    bool Less<Date*>(Date* left, Date* right)
    {
    	return *left < *right;
    }
    
    int main1()
    {
    	cout << Less(1, 2) << endl;
    	Date d1(2022, 7, 7);
    	Date d2(2022, 7, 8);
    	cout << Less(d1, d2) << endl;
    	Date* p1 = &d1;
    	Date* p2 = &d2;
    	cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
    	return 0;
    }
    
  • 缺陷(“大坑”):

    如果基础模板参数是const类型,而特化参数为指针,要特别小心注意将const放在指针的*号后面( const 在 * 前修饰的指针指向的对象,*后修饰的是指针本身。我们要的当然是对指针本身进行const修饰)

    //基础模板
    template<class T>
    bool Less(const T& left, const T& right)
    {
    	return left < right;
    }
    
    //特化
    template<>
    bool Less<Date*>(Date* const& left, Date* const& right) //注意Date*与const的位置关系
    {
    	return *left < *right;
    }
    
    void less_test()
    {
    	Date* p1;
    	Date* p2;
    
    	Less(p1, p2);
    }
    
    • 因此,对于上述情况,不建议使用函数模板。建议直接重载一份现成的函数,手动控制逻辑,以免出错。(使用模板时,我们很容易直接将T替换为Date*,而进行重载也许可以避开使用const,就算避不开也可以强迫我们进行代码逻辑的梳理,比较容易发现“坑点”)

      //函数重载
      bool Less(Date* left, Date* right)
      {
      	return *left < *right;
      }
      

3. 类模板特化

3.1 全特化

  1. 语法:

    1. 关键字template后面接一对空的尖括号<>
    2. 类名后跟一对尖括号,尖括号中指定需要特化的类型
  2. 概念:

    将模板参数列表中所有的参数都确定化。

    //基础模板
    template<class T1, class T2>
    class Data
    {
    public:
    	Data() { cout << "Data<T1, T2>" << endl; }
    private:
    	T1 _d1;
    	T2 _d2;
    };
    
    //全特化类模板
    template<>
    class Data<int, char>
    {
    public:
    	Data() { cout << "Data<int, char>" << endl; }
    private:
    	int _d1;
    	char _d2;
    };
    
    void TestVector()
    {
    	Data<int, int> d1; //调用基础模板
    	Data<int, char> d2; //调用全特化模板
    }
    

3.2 偏特化 / 半特化

  1. 部分参数特化

    // 将第二个参数特化为int
    template <class T1>
    class Data<T1, int>
    {
    public:
    	Data() { cout << "Data<T1, int>" << endl; }
    private:
    	T1 _d1;
    	int _d2;
    };
    
  2. 特化某个宽泛类型(如所有的指针、所有的引用)

    //两个参数偏特化为指针类型
    template <typename T1, typename T2>
    class Data <T1*, T2*>
    {
    public:
    	Data() { cout << "Data<T1*, T2*>" << endl; }
    private:
    	T1 _d1;
    	T2 _d2;
    };
    
    //两个参数偏特化为引用类型
    template <typename T1, typename T2>
    class Data <T1&, T2&>
    {
    public:
    	Data(const T1& d1, const T2& d2)
    		: _d1(d1)
    		, _d2(d2)
    	{
    		cout << "Data<T1&, T2&>" << endl;
    	}
    private:
    	const T1& _d1;
    	const T2& _d2;
    };
    
    void test2()
    {
    	Data<int*, int*> d3; // 调用特化的指针版本
    	Data<int&, int&> d4(1, 2); // 调用特化的指针版本
    }
    

    注意:

    1. class Data <T1*, T2*> 中的<T1*, T2*> 只是个标识符,并不是只指一级指针,而是指所有指针

    2. T1,T2的类型就是传入参数的类型,但是在类内使用时会被替换为T1去掉一个*号后的类型(一级指针变为非指针,二级指针变为一级指针)

      • 运行如下代码可以发现,T1,T2大小为4或者8,说明是一个指针,而打印它们的类型却显示为去掉了一个*后的类型,说明在类中使用时经过了处理
      //两个参数偏特化为指针类型
      template <typename T1, typename T2>
      class Data <T1*, T2*>
      {
      public:
      	Data()
      	{
      		cout << "Data<T1*, T2*>" << endl;
      		cout << sizeof(T1) << " " << sizeof(T2) << endl;
      		cout << typeid(T1).name() << " " << typeid(T2).name() << endl;
      		cout << typeid(_d1).name() << " " << typeid(_d2).name() << endl;
      	}
      private:
      	T1 _d1;
      	T2 _d2;
      };
      
      void test2()
      {
      	Data<int*, int*> d3; // 调用特化的指针版本
      	Data<int**, int**> d5; // 调用特化的指针版本
      }
      

3.3 应用示例

  1. 场景引入:

    STL中的Priority_queue默认的仿函数只能对最基础的类型进行大小比较,然后按堆规则排序,如果需要对特定类型进行大小比较,需要用户手动传入一个专用的仿函数进行特殊处理。

    同样都是特殊处理,我们能不能使用特化代替专用的仿函数?

  2. 代码对比:

    • 传入专门的仿函数:

      priority_queue < Date*, vector<Date*>, PDateless > q1;

      // 模拟priority_queue中的缺省仿函数
      template<class T>
      class myless
      {
      public:
      	bool operator()(const T& x, const T& y)
      	{
      		return x < y;
      	}
      };
      
      //针对指针类型作对象时 手动传入的仿函数
      struct PDateLess
      {
      	bool operator()(Date* p1, Date* p2)
      	{
      		return *p1 < *p2;
      	}
      };
      
    • 特化缺省仿函数:

      // 缺省提供的仿函数
      template<class T>
      class myless
      {
      public:
      	bool operator()(const T& x, const T& y)
      	{
      		return x < y;
      	}
      };
      
      //对其特化版本,使之符合指针对象作元素时的比较
      template<>
      class myless<Date*>
      {
      public:
      	bool operator()(Date* const & x, Date* const & y) //也存在函数模板特化时的问题
      	{
      		return *x < *y;
      	}
      };
      

4. 小结

  1. 函数模板特化实例化后就相当于函数重载
  2. 类模板特化实例化就“相当于重载了一个类”,务必注意c++语法中没有类重载这个概念,只是效果看起来可以这么理解

五、模板的分离编译

1. 分离编译的概念

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件 ,其中共用的代码(函数)一般采用声明和定义分离的方式,使用时源文件中只包含其头文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

2. 模板不能分离编译

  1. 分离编译的实现原理

    C/C++的编译过程分为预处理、编译、汇编、链接四个过程,其中编译是进行语法错误检查的,检查无误后才生成汇编代码。(头文件不参与编译)

    当函数经过编译时,如果只有声明,编译器此时是找不到函数地址的,但是认为后续链接时可以找到,所以会生成一个call xxxx的汇编指令,将其函数名放入到符号表中,让编译通过,生成 .obj文件。正常情况下,链接时 编译器会扫描各个符号表,寻找那些只有声明的那些函数的实际地址并将其替换到相应位置,然后再生成可执行文件。

    • 符号表:

      它是编译器在编译过程中创建的一种数据结构,用于存储变量名和内存地址之间的映射关系。主要作用是在编译时期将变量名转换为内存地址。当声明一个变量时,编译器会为其分配内存,并将变量名和分配的内存地址记录在符号表中。

      例如,声明 int a; 后,编译器可能会为 a 分配一个内存地址 0x0040,并将 a0x0040 的映射关系保存在符号表中。这样,在程序中对 a 进行操作时,编译器就可以通过符号表找到 a 的地址

    但对于模板来说,C++标准明确表示,当一个模板不被用到的时侯它就不该被实例化出来即 按需实例化,这会导致什么呢?假设我们在 main.cpp中调用了函数模板,但是只有该函数的声明,定义在text.cpp文件中实现。正常情况链接时编译器会从text.cpp的符号表中找到该函数的地址,然后给main.cpp调用,可是我们刚说了模板是按需实例化,即text.cpp文件中即使有定义,但是没有在其中调用就不会被实例化,没有实例化就没有地址(没有开辟物理空间),那编译器不就是找了个寂寞吗?自然就会报错说找不到地址。

    实际上就是想用的地方(main.cpp)调用不到,不需要用的地方(text.cpp)又需要调用才能实例化给想用的地方传地址

    • 按需实例化:

      模板、类成员函数,没有被调用时编译器不会对其进行实例化,不会对其进行编译。如果代码写的有问题则编译时不会语法报错,但是链接时如果被调用就可能会出错

      可以认为它们相较于普通函数,多了一步实例化的过程

    //-------------text.h----------------//
    
    void func(); // 函数声明
    
    //---------------text.cpp-------------//
    
    #include"text.h"
    
    template<class T>
    void func(T left, T right)  //函数定义 
    {
    	cout << "func被实现" << endl;
    }
    
    int main()
    {
    	return 0; //没有调用函数模板,不会进行实例化
    }
    
    //---------------main.cpp---------------//
    
    #include"text.h"
    
    int main()
    {
    	func(1, 2); // 调用函数模板
    }
    
  2. 解决方法
    1. 将声明和定义放到一个文件 xxx.h 中

      • 会重定义吗?不会,因为c++标准中明确规定了编译器可以丢弃部分语句,其中就包括了相同的模板实例,会从中随机选取一份保留,其余丢弃。简而言之,就是编译器给模板开后门进行了特殊处理。

        那理论上普通函数也可以这么处理,不就不存在重定义问题了?是这样的,但是C++有C的历史包袱,祖宗之法不可变,不然会有兼容问题

    2. 显式实例化

      • 根据调用时的实际参数将模板实现,就相当于一个普通函数,自然会被实例化
      • 不建议这样使用,每使用一个新类型就要显示实例化一次(本来使用模板就是为了省事,这样不就是一个个进行函数重载吗?模板此时啥用没有,全是用户手动在操作)
      //---------------text.cpp-------------//
      
      #include"text.h"
      
      template<class T>
      void func(T left, T right)  //函数定义
      {
      	//...
      }
      
      //显式实例化,每调用一个新类型都要补上一次显式实例化
      void func(int left, int right) 
      {
      	//...
      }
      //---------------main.cpp---------------//
      
      #include"text.h"
      
      int main()
      {
      	func(1, 2); // 调用函数模板
      }
      

六、模板总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

总结

本文讲解了模板的相关使用和常见误区。
尽管文章修正了多次,但由于水平有限,难免有不足甚至错误之处,敬请各位读者来评论区批评指正。

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

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

相关文章

秋招面试题记录_半结构化面试

c八股(可能问的多一点) 1.简单说说C11语法特性 答&#xff1a; 1.auto以及decltype自动类型推导&#xff0c;避免手动声明复杂类型&#xff0c;减少冗长代码提升了可读性和安全性。 2.智能指针 自动释放内存 (具体说说) 有shared和unique 差异主要体现在所有权、内存开销、…

微积分复习笔记 Calculus Volume 1 - 3.4 Derivatives as Rates of Change

3.4 Derivatives as Rates of Change - Calculus Volume 1 | OpenStax

京东 北京 java 中级: 哪些情况下的对象会被垃圾回收机制处理掉? 哪些对象可以被看做是 GC Roots 呢?对象不可达,一定会被垃圾收集器回收么?

我同学最近在面试java的岗位, 这是他遇到的某些关于java的JVM中垃圾回收相关的部分的问题, 他来问我, 我特以此文章来解答. 公司 京东 base 北京 面试时间 2024年10月23日16:00:00 他跟我说, 面试官一上来就问了一个关于JVM的问题, 直接就给他难住了, 问题是 : 哪些情况下…

数据结构《顺序表》

文章目录 前言一、什么是顺序表&#xff1f;1.1 顺序表的概念1.2 顺序表的建立 二、MyArrayList的实现三、顺序表的方法四、关于顺序表的例子总结 前言 提示&#xff1a;这里涉及到的ArrayList类是一个泛型类&#xff0c;同时后面的很多内容都会涉及到泛型&#xff0c;如果不了…

【蓝队技能】【内网隧道工具流量分析】FRPNPSreGeorgVenom

蓝队技能 FRP&NPS&reGeorg&Venom 蓝队技能总结前言一、FRP1.1 流量分析1.2 特征提取 二 NPS1.1 流量分析1.2 特征提取 三、reGeor1.1 特征提取 四、Venom1.1 特征提取 总结 前言 本文聚焦内网隧道代理技术&#xff0c;涵盖Frp、Nps、Neo-reGeorg及Venom等工具。这些…

潜水定位通信系统的功能和使用方法_鼎跃安全

潜水定位通信系统是保障潜水安全与作业高效的关键设备。它利用先进的声呐、无线电等技术&#xff0c;可精准定位潜水员位置。在水下能实现潜水员之间以及与水面的双向通信&#xff0c;确保信息及时传递。具备高可靠性和稳定性&#xff0c;即使在复杂水环境中也能正常运行。 一、…

Git Push(TODO)

最近经常碰到GIT push不上去的问题。到处求人解决也真是尴尬&#xff0c;想自己看看&#xff0c;所以刚刚在github上建了一个仓&#xff0c;试了下。结果如下&#xff1a; 暂时可能还不行&#xff0c;因为数据都是加密的&#xff0c;没法看到具体GIT的交互信息。。。 后面再想办…

算法的学习笔记—两个链表的第一个公共结点(牛客JZ52)

&#x1f600;前言 在链表问题中&#xff0c;寻找两个链表的第一个公共结点是一个经典问题。这个问题的本质是在两个单链表中找到它们的相交点&#xff0c;或者说它们开始共享相同节点的地方。本文将详细讲解这个问题的解题思路&#xff0c;并提供一种高效的解决方法。 &#x…

WPFDeveloper正式版发布

WPFDeveloper WPFDeveloper一个基于WPF自定义高级控件的WPF开发人员UI库&#xff0c;它提供了众多的自定义控件。 该项目的创建者和主要维护者是现役微软MVP 闫驚鏵: https://github.com/yanjinhuagood 该项目还有众多的维护者&#xff0c;详情可以访问github上的README&…

Golang | Leetcode Golang题解之第497题非重叠矩形中的随机点

题目&#xff1a; 题解&#xff1a; type Solution struct {rects [][]intsum []int }func Constructor(rects [][]int) Solution {sum : make([]int, len(rects)1)for i, r : range rects {a, b, x, y : r[0], r[1], r[2], r[3]sum[i1] sum[i] (x-a1)*(y-b1)}return Sol…

SpringBoot项目ES6.8升级ES7.4.0

SpringBoot项目ES6.8.15 升级到 ES7.4.0 前言 由于公司内部资产统一整理&#xff0c;并且公司内部部署有多个版本的es集群&#xff0c;所以有必要将目前负责项目的ES集群升级到公司同一版本7.4.0。es6到es7的升级变化还是挺大的&#xff0c;因此在这里做一下简单记录&#xf…

从新手到高手:map和set的使用技巧全攻略(C++)

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C&#xff1a;由浅入深篇 小新的主页&#xff1a;编程版小新-CSDN博客 前言&#xff1a; 本章节讲解的map和set底层…

C++新基础类型(C++11~C++20)

本文章记录C11~C20的新基础类型。 1.整数类型long long long long类型是C11引入的。在C标准中规定&#xff0c;long long是一个至少为64位的整数类型。可以使用LL和ULL字面量后缀进行初始化。格式化打印时分别用%lld和%llu。 long long x 12345LL; unsigned long long y 4…

简单的windows java -jar 无法启动jar包解决方法

简单的windows java -jar 无法启动jar包解决方法 1. 问题 我们项目是使用nacos作为注册中心以及配置中心&#xff0c;我们本地使用idea 进行服务配置以及启动发现没有问题&#xff0c;然后我们的服务经过maven install 打包后发布到LINUX服务启动也没有问题&#xff0c;但是我…

Artistic Oil Paint 艺术油画着色器插件

只需轻轻一点&#xff0c;即可将您的视频游戏转化为艺术品&#xff01;&#xff08;也许更多…&#xff09;。 ✓ 整个商店中最可配置的选项。 ✓ 六种先进算法。 ✓ 细节增强算法。 ✓ 完整的源代码&#xff08;脚本和着色器&#xff09;。 ✓ 包含在“艺术包”中。 &#x1f…

上拉电阻和下拉电阻在电路中的作用(一)

上拉电阻和下拉电阻在电路中的作用&#xff08;一&#xff09; 1.什么是上下拉电阻2.上下拉电阻的作用&#xff1a;2.1.维持输入引脚处于稳定状态。2.2.配合三极管和MOS进行电平转换电路设计2.3.OC、OD电路&#xff08;Open Collector集电极开路、Open Drain漏电极开路&#xf…

优化UVM环境(九)-将interface文件放在env pkg外面

书接上回&#xff1a; 优化UVM环境&#xff08;八&#xff09;-整理project_common_pkg文件 My_env_pkg.sv里不能包含interface&#xff0c;需要将my_intf.sv文件放在pkg之外

Leetcode 1135. 最低成本连通所有城市

1.题目基本信息 1.1.题目描述 想象一下你是个城市基建规划者&#xff0c;地图上有 n 座城市&#xff0c;它们按以 1 到 n 的次序编号。 给你整数 n 和一个数组 conections&#xff0c;其中 connections[i] [x_i, y_i, cost_i] 表示将城市 x_i 和城市 y_i 连接所要的cost_i&…

【scene_manager】与 MoveIt 机器人的规划场景进行交互

scene_manager Scene Manager包是由 Robotnik 创建的 ROS 包&#xff0c;旨在帮助构建和与 MoveIt 机器人的规划场景进行交互。 背景信息 MoveIt 规划场景 是一个用于存储机器人周围世界的表示&#xff08;外部碰撞&#xff09;以及机器人自身状态&#xff08;内部碰撞和当…

LeetCode.102 二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 提示&#xff1a; 树中节点数目在范围 [0, 2000] 内-1000 < Node.val < 1000 解题思路 对二叉树进行层序遍历即可&am…