【C++】——继承详解

目录

1、继承的概念与意义

2、继承的使用

2.1继承的定义及语法

2.2基类与派生类间的转换

2.3继承中的作用域

2.4派生类的默认成员函数

<1>构造函数

<2>拷贝构造函数

<3>赋值重载函数

<4析构函数

<5>总结

3、继承与友元

4、继承与静态变量

5、菱形继承及菱形虚拟继承

6、继承与组合


1、继承的概念与意义

什么是继承?

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。

继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。

通过继承联系在一起的类构成了一种层次关系,在这种关系中有一个基类(base class),其他类则是直接或间接地从基类继承过来的,这些继承来的类可以称为派生类(drived class)。基类通常有着层次关系中所有类共同拥有维护的成员,而每个派生类也有着自己各自特定的成员。

一个简单的例子:一个学习管理系统,那么成员必定有学生,老师等等,这些是身份,归根到底是个人(基类)包含着名字、年龄、地址等基础信息。这些需要共同维护的就是基类的成员。

//共同维护的成员部分->基类
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "qsy"; // 姓名 string _address; // 地址 string _tel; // 电话 int _age = 18; // 年龄 
};class Student : public Person
{
public:// 学习 void study(){// ...}
protected:int _stuid; // 学号 
};
class Teacher : public Person
{
public:// 授课 void teaching(){//...}
protected:string title; // 职称 
};
int main()
{Student s;Teacher t;s.identity();t.identity();return 0;
}

可以看到派生类可以访问基类成员

如果没有继承这种结构关系的话 Student和Teacher 都有姓名/地址/ 电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。更好地体现了继承是类设计层次的复用。

2、继承的使用

2.1继承的定义及语法

这就是继承的语法格式

继承方式与访问限定符号一样有着三种,不同的继承方式与不同的类成员组合会是不同的情况

总结一下规律:

<1>基类 private 成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

将年龄变为私有验证一下是否继承到了派生类对象

可以看到继承下来了但是不可以访问!!

<2>如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为 protected。可以看出保护成员限定符是因继承才出现的。

如果想要访问 private 成员可以在基类中成员函数访问,这样派生类可以间接访问到 private成员

<3>基类的私有成员在派生类都是不可见。基类的其他成员在派生类的访问方式 == Min(成员在基类的访问限定符,继承方式),public  > protected > private。

Tip:class默认继承方式是 private,struct默认继承方式是public。最好显示写出继承方式

<4>在实际运用中⼀般使用都是 public 继承,几乎很少使用 protetced/private 继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实 际中扩展维护性不强。

2.2基类与派生类间的转换

基类与派生类之间是否有着类型的转换呢?

答案是可以的! public继承中有一个 is-a 概念:每个派生类都是一个特殊的基类对象

• public继承的派生类对象 可以赋值给 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中基类那部分切出来,基类指针或引用指向的是派生类中切出来的基类那部分。

基类对象不能赋值给派生类对象

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type  Information)的dynamic_cast 来进行识别后进行安全转换。

2.3继承中的作用域

继承体现中也有各自的作用域规则并且引出来一个隐藏概念,隐藏影响的只是编译器查找规则

1. 在继承体系中基类和派生类都有独立的作用域。

2. 派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派生类成员函数中,可以使用 基类::基类成员显示访问)

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。(区分重载)

4. 注意在实际中在继承体系里面最好不要定义同名的成员。

// Student的_num 和 Person的_num 构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆 
class Person
{
protected:string _name = "小徐"; // 姓名 int _num = 111; // ⾝份证号 
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份证号:" << Person::_num << endl;cout << " 学号:" << _num << endl;}
protected:int _num = 999; // 学号 
};
int main()
{Student s1;s1.Print();return 0;
};

访问的是哪个 _num 呢?

可以看到派生类成员隐藏了基类的同名成员,直接访问了派生类的 _num 

同理,函数也有隐藏的现象

A和B类中的 fun 两个函数构成什么关系呢??根据前面的知识可以知道继承体系中函数名相同就构成隐藏关系

2.4派生类的默认成员函数

6个默认成员函数,默认的意思就是指我们不写,编译器会自动生成⼀个,那么在派生类中,这 几个成员函数是如何生成的呢?

四个常见默认成员函数:

<1>构造函数

派生类的构造函数必须调用基类的构造函数初始化基类的那⼀部分成员。

class Person
{
public:Person(const char* name="xxc") //全缺省函数,默认构造:_name(name){cout << "Person()" << endl;}
protected:string _name;//姓名
};class Student :public Person
{
public://不显示实现默认构造,编译器生成的// 1. 内置类型->不确定// 2. 自定义类型->调用自定义类型的显示写的默认构造// 3. 基类成员看作一个整体,要求调用基类的默认构造
protected:int _num;//学号string _addrss;//地址
};int main()
{Student s1;return 0;
}

如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用

写一个 Student的构造函数

还是报错!前面提到 需要把基类成员当成一个对象调用基类的构造函数

如何实现一个不能被继承的类呢?

方法1:基类的构造函数私有,派生类的构成必须调用基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。

方法2:C++11新增了⼀个final关键字,final 修改基类,派生类就不能继承了。

<2>拷贝构造函数

派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

可以看到,没有资源申请的时候 Student 并不需要自己显示实现拷贝构造,因为编译器默认拷贝构造会调用基类的拷贝构造

那么怎么自己实现拷贝构造呢?(Tip:基类对象是最先声明(内存顺序)的,初始化列表中第一个初始化)

Person(const Person& p): _name(p._name)
{cout << "Person(const Person& p)" << endl;
}Student(const Student& s):Person(s),_num(s._num),_addrss(s._addrss)
{//深拷贝
}

Person(s) 这个 s 是派生类对象的引用为什么可以传给基类呢? 涉及基类与派生类间的转换概念——切片 

如果显示写了拷贝构造但是不显示调用基类的拷贝构造的会,编译器会自动调用默认构造而非调用基类的拷贝构造

补充一下缺省值构成默认构造,运行一下发现调用的就是默认构造而非拷贝构造

<3>赋值重载函数

派生类的operator=必须要调用基类的operator=完成基类的复制。

赋值重载与拷贝构造类似一般编译器默认生成的就已经够用了,如果有资源申请的话才需要显示实现

Student& operator=(const Student& s)
{if (this != &s){operator=(s);//派生类切片基类成员_num = s._num;_addrss = s._addrss;}return *this;
}

栈溢出,无限递归调用,我们不是想要调用基类的赋值函数吗?为什么调用了派生类的呢?

需要注意的是派生类的 operator= 隐藏了基类的operator= ,所以显示调用基类的operator= ,需要指定基类作用域

Student& operator=(const Student& s)
{if (this != &s){//基类和派生类的赋值构成了隐藏关系 需要指定作用域Person::operator=(s);//派生类切片基类成员_num = s._num;_addrss = s._addrss;}return *this;
}

<4析构函数

析构函数可以显示调用,那么可以在派生类显示调用基类的析构函数来清理基类成员

可是为什么调不动呢?这里派生类和基类的析构函数构成了隐藏关系

因为多态中⼀些场景析构函数需要构成重写,重写的条件之一是函数名相同,那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。

想要调用就标明作用域:

Person::~Person()

但是像上述这样写,会有一个问题,基类的析构会调用两次!!!

其实,派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。所以我们不必在派生类的析构函数中进行调用基类的析构函数,不然就会重复释放同一块空间,导致报错!

可以怎么理解派生类析构自动调用基类的析构呢? 先子后父保证析构顺序!显示调用不一定保证先子后父的析构顺序

<5>总结

派生类和基类的层次关系逻辑基础还是类和对象

派生类的默认成员函数的注意事项:

<1>派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。


<2>派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。


<3>派生类的operator=必须要调用基类的operator=完成基类的复制。


<4>派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员(不需要显示和调用基类析构)。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。


<5>派生类对象初始化先调用基类构造再调派生类构造。派生类对象析构清理先调用派生类析构再调基类的析构。


<6>因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系

3、继承与友元

友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员。比如爸爸的朋友可以说是你的朋友吗?


class Student;//前置声明
class Person
{
public:friend void Display(const Person& p, const Student& s);//需要前置声明否则报错招不到 Student
protected:string _name; // 姓名 
};
class Student : public Person
{
protected:int _stuNum; // 学号 
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;
}
int main()
{Person p;return 0;
}

如果访问派生类的私有和保护成员呢?

可以看见是不可访问的 在派生类同样设置一个友元就可以解决这个问题了。

4、继承与静态变量

基类定义了 static 静态成员,则整个继承体系里面只有⼀个这样的成员。无论派生出多少个派生类,都只有⼀个static 成员实例。

验证一下:

class A
{
public: static int _a;int _aa;
};
class B :public A
{
public:int _b;
};
// static int _a = 1;报错int A::_a = 1;//注意定义的方式
int main()
{A  a;B b1;B b2;//这⾥的运行结果可以看到非静态成员_aa的地址是不⼀样的// 说明派生类继承下来了,⽗类派生类对象各有⼀份 cout << &a._aa << endl;cout << &b1._aa << endl;cout << endl;// 这⾥的运行结果可以看到静态成员 _a 的地址是⼀样的 //说明派生类和基类共用同⼀份静态成员 cout << &a._a << endl;cout << &b1._a << endl;cout << &b2._a << endl;cout << endl;//公有情况下 基类派生类都可以访问静态成员变量cout << a._a << endl;cout << b1._a << endl;cout << b2._a << endl;return 0;
}

也就说明他们共用一个_a变量,所以无论派生出多少个子类,都只有一个static成员实例

这个特性可以带来一种思路统计实例化类的数量个数,只需在构造函数中加入一个增加该静态变量的语句即可:

class Person
{
public:Person() { ++_count; }//子类的构造会调用父类构造
protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};
int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};
int main()
{Student s1;Student s2;Student s3;cout << " 人数 :" << Person::_count << endl;Student::_count = 0;cout << " 人数 :" << Person::_count << endl;return 0;
}

这样我们就可以知道该继承体系中实例化了多少个类了!!!

5、菱形继承及菱形虚拟继承

首先声明一下,由于C++的历史缘故,其一致行走在语言发展的前端,一直在尝试新的内容。在发展过程中,有些内容加入到C++的时候,还没有发现其弊端。而后来发现的时候,为了向上兼容,只能打补丁,所以不开避免的不会有一些弊端,会有复杂的语法和复杂的特性。总要有先驱者走前前面,而C++就是!!!

单继承:⼀个派生类只有⼀个直接基类时称这种继承关系为单继承

多继承:⼀个派生类有两个或以上直接基类时称这种继承关系为多继承多继承对象在内存中的模型是,先继承的基类在前面,后面继承的基类在后面,派生类成员在放到最后面。

菱形继承:菱形继承是多继承的⼀种特殊情况。

菱形继承的问题,从上面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。

class Person
{
public:string _name; // 姓名 
};
class Student : public Person
{
protected:int _num; //学号 
};
class Teacher : public Person
{
protected:int _id; // 职⼯编号 
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程 
};
int main()
{// 编译报错:error C2385: 对“_name”的访问不明确 二义性Assistant a;a._name = "peter";// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

那该如何解决数据冗余的问题呢??可以借用虚拟继承!!

虚拟继承(virtual)可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在StudentTeacher的继承Person时使用虚拟继承,即可解决问题。

这是什么原理呢?测试一下!

菱形继承不虚拟继承的情况

#include<iostream>
#include<string>using namespace std;class A
{
public:int _a;
};class B : public A
//class B : virtual public A
{
public:int _b;
};class C : public A
//class C : virtual public A
{
public:int _c;
};
class D : public B, public C
{
public:int _d;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

调试一下:

通过这个逐语句调试的内存变化,我们可以确定大致的内存情况:

不使用虚拟继承就是这样的内存情况,也好理解为什么同名变量的两份是如何储存的了。
接下来我们来看虚拟继承下的菱形继承是怎么个情况:

内存分布:

a储存在最下面,而B,C部分的原有储存_a的位置现在是什么呢???
其实是个指针,那我们来看看指针指向的空间储存着什么吧:

???怎么对应位置是00 00 00 00为什么是零?往下看看:
分别储存着16进制数字14 0c转换为10进制数字20 12,然后对应B,C原本的指针位置(006FFB6C)加上这个值(偏移量),都会指向到A _a的空间!!!这个00 00 00 00到多态的部分再来进行讲解,知道原地址加上下面的值就是A _a的空间就可以了!!!

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
即原本B,C中_a的位置储存这一个指针,指针指向的位置有一个偏移量,原位置的地址加上偏移量就会指向A的空间!!!

那这样进行拷贝切片的时候是怎样的呢?一样是把D中B对象的部分切片,然后通过虚基表的方式来找到_a。但这样也带来了一些代价:(PS:内存中的储存顺序就是声明的顺序,先继承谁,谁就在前面)

多继承指针偏移问题(切片)

p1和p2指向哪里呢???

内存分布中,先继承的放前面!

因为切片的概念p2指向 base2开始但是只能看见 base2 那一部分

6、继承与组合

  • public继承是一种is-a(谁是什么)的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种has-a(谁有什么)的关系。假设B组合了A,每个B对象中都有一个A对象(也就是把A作为B的成员变量)
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse 能看见,不安全,耦合度高)。术语 “白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。


  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse 不能能看见,安全,耦合度低),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

有关继承的经典面试题
<1>C++有多继承,为什么java等语言没有?
历史原因!C++是先驱者(人的直觉认为多继承很合理,我感觉正常人都会想到多继承),并且c++中的多继承处理起来十分复杂,访问基类变量的过程就会很复杂!!!java等后来发展的语言见到c++中多继承的复杂,就干脆放弃了。

<2>什么是菱形继承?多继承的问题是什么?
菱形继承如字面意思(两个父类的父类是同一个类就会发生菱形继承),多继承本身没什么问题,真正的问题是有多继承就可能发生菱形继承。菱形继承就有问题了:变量的二义性和继承冗杂。解决办法很简单就是虚拟继承,但是这样就会大大降低效率。

<3>继承和组合的区别?什么时候用继承?什么时候用组合?
继承:通过扩展已有的类来获得新功能的代码复用方法
组合:新类由现有类的对象合并而成的类的构造方式

如果二者间存在一个“是”的关系,并且一个类要对另外一个类公开所有接口,那么继承是更好的选择
如果二者间存在一个“有”的关系,那么首选组合
!能用组合就用组合!!!能用组合就用组合!!!能用组合就用组合!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用ESP8266和OLED屏幕实现一个小型电脑性能监控

前言 最近大扫除&#xff0c;发现自己还有几个ESP8266MCU和一个0.96寸的oled小屏幕。又想起最近一直想要买一个屏幕作为性能监控&#xff0c;随机开始自己diy。 硬件&#xff1a; ESP8266 MUColed小屏幕杜邦线可以传输数据的数据线 环境 Windows系统Qt6Arduino Arduino 库…

【蔡英丽医生】小细节大影响:解读血栓来临前的身体语言!

血栓&#xff0c;这一隐形的健康杀手&#xff0c;常常在不经意间悄然降临&#xff0c;给人们的健康带来严重威胁。了解血栓来临前的身体语言&#xff0c;对于及早预防和治疗至关重要。今天&#xff0c;我们特别邀请到北京中医药大学东方医院脑病科的副主任医师——蔡英丽医生&a…

极速上云2.0范式:一键智连阿里云

在传统上云的现状与挑战&#xff1a; 专线上云太重&#xff0c;VPN上云不稳&#xff0c;云上VPC&#xff0c;云下物理网络&#xff0c;多段最后一公里...... 层层对接&#xff0c;跳跳延迟&#xff0c;好生复杂! 当你试图理解SD-WAN供应商和阿里云的文档&#xff0c;以协调路由…

介绍一些免费 的 html 5模版网站 和配色 网站

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、H5 网站介绍网站 二、配色网站个人推荐 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、H5 网站介绍 以下是一些提供免费…

构建响应式 Web 应用:Vue.js 基础指南

构建响应式 Web 应用&#xff1a;Vue.js 基础指南 一 . Vue 的介绍1.1 介绍1.2 好处1.3 特点 二 . Vue 的快速入门2.1 案例 1 : 快速搭建 Vue 的运行环境 , 在 div 视图中获取 Vue 中的数据2.2 案例 2 : 点击按钮执行 vue 中的函数输出 vue 中 data 的数据2.3 小结 三 . Vue 常…

PHP安全

PHP伪协议&#xff1a; 一.【file://协议】 PHP.ini&#xff1a; file:// 协议在双off的情况下也可以正常使用&#xff1b; allow_url_fopen &#xff1a;off/on allow_url_include&#xff1a;off/on file:// 用于访问本地文件系统&#xff0c;在CTF中通常用来读取本地文…

如何用安卓玩Java版Minecraft,安卓手机安装我的世界Java版游戏的教程

安卓手机使用FCL启动器安装我的世界Java版游戏的教程。如何用安卓玩Java版Minecraft 视频教程&#xff1a;https://www.bilibili.com/video/BV1CctYebEzR/ 前言 目前&#xff0c;安卓设备上可以用来运行Java版Minecraft的启动器主要有以下几款&#xff1a; PojavLauncher&a…

dedecms(四种webshell姿势)、aspcms webshell漏洞复现

一、aspcms webshell 1、登陆后台&#xff0c;在扩展功能的幻灯片设置模块&#xff0c;点击保存进行抓包查看 2、在slideTextStatus写入asp一句话木马 1%25><%25Eval(Request(chr(65)))%25><%25 密码是a&#xff0c;放行&#xff0c;修改成功 3、使用菜刀工具连…

第十一章 【后端】商品分类管理微服务(11.3)——商品管理模块 yumi-etms-goods

11.3 商品管理模块 yumi-etms-goods 新建 yumi-etms-goods 模块 添加依赖 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns&#

【数字集成电路与系统设计】Chisel/Scala简介与Verilog介绍

目录 一、芯片前端设计开发背景知识 二、Verilog介绍 2.1 硬件设计一些重要概念 2.2 功能性仿真 2.3 简单的Verilog代码例子&#xff08;4-bit的加法器&#xff09; 三、Chisel简介 3.1 Chisel基本概念 3.2 Chisel代码展示 3.3 Chisel转成Verilog代码 四、Scala入…

Notepad++插件:TextFX 去除重复行

目录 一、下载插件 TextFX Characters 二、去重实操 2.1 选中需要去重的文本 2.2 操作插件 2.3 结果展示 2.3.1 点击 Sort lines case sensitive (at column) 2.3.2 点击 Sort lines case insensitive (at column) 一、下载插件 TextFX Characters 点【插件】-【插件管理…

数学学习记录

目录 学习资源&#xff1a; 9月14日 1.映射&#xff1a;​编辑 2.函数: 9月15日 3.反函数&#xff1a; 4.收敛数列的性质 5.反三角函数&#xff1a; 9月16日 6.函数的极限&#xff1a; 7.无穷小和无穷大 极限运算法则&#xff1a; 学习资源&#xff1a; 3Blue1…

【Elasticsearch系列九】控制台实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【matlab】生成 GIF 的函数(已封装可直接调用)

文章目录 前言一、函数输入与输出二、函数代码三、例程&#xff08;可直接运行&#xff09;参考文献 前言 生成 gif 图片时遇到的问题&#xff0c;为了后续调用方便&#xff0c;封装为函数 一、函数输入与输出 输入&#xff1a; cell_figure: cell 数组&#xff0c;数组元素是…

热成像目标检测数据集

热成像目标检测数据集 V2 版本 项目背景 热成像技术因其在安防监控、夜间巡逻、消防救援等领域的独特优势而受到重视。本数据集旨在提供高质量的热成像图像及其对应的可见光图像&#xff0c;支持热成像目标检测的研究与应用。 数据集概述 名称&#xff1a;热成像目标检测数据…

CSS框架 Tailwind CSS

文章目录 前言一、Tailwind CSS是什么&#xff1f;二、项目中如何使用1.安装Tailwind CSS2.初始化Tailwind CSS该处使用的url网络请求的数据。3.引入Tailwind CSS样式4.进行配置&#xff08;tailwind.config.js&#xff09;5.全局引入注册6.使用Tailwind CSS 总结 前言 Tailwi…

IP-adapter masking

https://github.com/huggingface/diffusers/issues/6802https://github.com/huggingface/diffusers/issues/6802

2024/9/16 dataloader、tensorboard、transform

一、pytorch两大法宝元素 假设有一个名为pytorch的包 dir()&#xff1a;用于打开包&#xff0c;看里面的内容 help():用于查看具体的内容的用处 二、python文件&#xff0c;python控制台和jupyter的使用对比 三、pytorch读取数据 pytorch读取数据主要涉及到两个类&#xff1…

开源 AI 智能名片链动 2+1 模式 S2B2C 商城小程序与社交电商的崛起

摘要&#xff1a;本文深入探讨了社交电商迅速发展壮大的原因&#xff0c;并分析了开源 AI 智能名片链动 21 模式 S2B2C 商城小程序在社交电商中的重要作用。通过对传统电商与社交电商的对比&#xff0c;以及对各发展因素的剖析&#xff0c;阐述了该小程序如何为社交电商提供新的…

基于web的 BBS论坛管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…