C++ 继承
目录
一、继承的概念及定义
1、继承的概念
2、继承定义
二、基类和派生类对象赋值转换
三、继承中的作用域
四、派生类的默认成员函数
五、继承与友元
六、继承与静态成员
七、复杂的菱形继承及菱形虚拟继承
1、菱形继承
2、虚拟继承
3、例题
八、继承的总结和反思
一、继承的概念及定义
1、继承的概念
- 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
- 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
#include <iostream>
#include <string>
using namespace std;class Person
{
public:void Print() {cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
private:string _name = "hhhhh";int _age = 666;
};class Student : public Person
{
private:int _stuid;
};int main()
{Student a;a.Print();
}
2、继承定义



(3)继承基类成员访问方式的变化
类成员/继承方式 | public继承 | protected继承 | private继承 |
基类的public成员 | 派生类的public成员 | 派生类的protected 成员 | 派生类的private 成员 |
基类的protected 成员 | 派生类的protected 成员 | 派生类的protected 成员 | 派生类的private 成员 |
基类的private成 员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可 见 |
总结:
1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。(可以调用父类的函数访问)
class Base {
private:int private_member;
};class Derived : public Base {// private_member 在这里是不可见的,无法访问
};int main() {Derived d;// d.private_member; // 错误:private_member 在派生类中是不可见的return 0;
}
2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
class Base {
protected:int protected_member;
};class Derived : public Base {
public:void access_member() {protected_member = 10; // 可以访问}
};int main() {Derived d;d.access_member(); // 可以访问// d.protected_member; // 错误:protected_member 在类外是不可访问的return 0;
}
3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式) , public > protected > private。
class Base {
public:int public_member;
protected:int protected_member;
private:int private_member;
};class Derived : protected Base {// public_member 在这里变为了 protected// protected_member 仍然是 protected// private_member 是不可见的
};int main() {Derived d;// d.public_member; // 错误:public_member 在派生类中是 protectedreturn 0;
}
4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public ,不过最好显示的写出继承方式。
class Base {
public:int member;
};class Derived1 : Base {// 默认为 private 继承
};struct Derived2 : Base {// 默认为 public 继承
};int main() {Derived1 d1;// d1.member; // 错误:member 在派生类中是 privateDerived2 d2;d2.member = 10; // 可以访问return 0;
}
5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强。
class Base {
public:int public_member;
};class Derived : public Base {// 通常使用 public 继承
};int main() {Derived d;d.public_member = 10; // 可以访问return 0;
}
二、基类和派生类对象赋值转换
- 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片 或者切割。寓意把派生类中父类那部分切来赋值过去。
- 基类对象不能赋值给派生类对象。
- 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
- 的指针是指向派生类对象时才是安全的。

class Person
{
protected :string _name; // 姓名string _sex; // 性别int _age; // 年龄
};
class Student : public Person
{
public :int _No ; // 学号
};
void Test ()
{Student sobj ;// 1.子类对象可以赋值给父类对象/指针/引用Person pobj = sobj ;Person* pp = &sobj;Person& rp = sobj;//2.基类对象不能赋值给派生类对象sobj = pobj;// 3.基类的指针可以通过强制类型转换赋值给派生类的指针pp = &sobjStudent* ps1 = (Student*)pp; // 这种情况转换时可以的。ps1->_No = 10;pp = &pobj;Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问
题ps2->_No = 10;
}
三、继承中的作用域
- 在继承体系中基类和派生类都有独立的作用域。
- 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
- 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
- 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
- 注意在实际中在继承体系里面最好不要定义同名的成员。
class Person
{
protected:string _name = "张三"; // 姓名int _num = 11; // 身份证号
};class Student : public Person
{
public:void Print(){cout << Person::_num << endl;cout << _num << endl;}
protected:int _num = 666; // 学号
};int main()
{Student s;s.Print();return 0;
}

例二 :
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)->" << i << endl;}
};void Test()
{B b;b.fun(10); //访问B中的funb.A::fun();//访问A中的funb.fun(); //报错
};
四、派生类的默认成员函数

- 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认 的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
也就是如果基类有默认构造函数(没有参数的构造函数),派生类的构造函数会自动调用基类的默认构造函数来初始化基类的成员。
然而,如果基类没有默认构造函数,即基类的构造函数需要参数,那么派生类的构造函数就必须在初始化列表阶段显式调用基类的构造函数,以提供必要的参数来初始化基类的成员。这样可以确保基类的成员在派生类对象创建时得到正确的初始化。
- 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
- 派生类的operator=必须要调用基类的operator=完成基类的复制。
- 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能 保证派生类对象先清理派生类成员再清理基类成员的顺序。
- 派生类对象初始化先调用基类构造再调派生类构造。
- 派生类对象析构清理先调用派生类析构再调基类的析构。
- 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲 解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加
- virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};class Student : public Person
{
public:Student(const char* name, int num):Person(name), _num(num){cout << "Student()" << endl;}Student(const Student& s):Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator=(const Student& s){if (this != &s){Person::operator=(s);_num = s._num;}cout << "Student& operator=(const Student& s)" << endl;return *this;}// 析构函数会被处理成destructor~Student(){//Person::~Person();//不需要显示调用,会多调用一次cout << "~Student()" << endl;}// 子类析构函数完成时,会自定调用父类析构函数,保证先析构子再析构父
protected:int _num; //学号
};
-
Person类:这是一个基类,包含一个保护成员变量_name,表示人的名字。它有一个带默认参数的构造函数,一个拷贝构造函数,一个赋值运算符重载函数和一个析构函数。在每个函数中,都使用了cout来打印函数的名称,以便于跟踪函数的调用。
-
Student类:这是一个从Person类派生的子类,增加了一个保护成员变量_num,表示学生的学号。它也有一个构造函数,一个拷贝构造函数,一个赋值运算符重载函数和一个析构函数。在构造函数和拷贝构造函数中,都会先调用父类的相应函数,然后再处理子类特有的部分。在赋值运算符重载函数中,也是先调用父类的赋值运算符重载函数,然后再处理子类特有的部分。在析构函数中,不需要显式调用父类的析构函数,因为子类的析构函数完成后,会自动调用父类的析构函数。
int main()
{Student s1("张三", 18);Student s2(s1);Person p = s1;s1 = s2;return 0;
}
在main函数中,以下是详细的调用过程:
-
Student s1("张三", 18);
:创建一个Student对象s1。首先,调用基类Person的构造函数,将_name初始化为"张三",然后打印"Person()"。接着,初始化派生类Student的成员_num为18,然后打印"Student()"。 -
Student s2(s1);
:使用s1创建一个新的Student对象s2。首先,调用基类Person的拷贝构造函数,将s2的_name初始化为s1的_name,然后打印"Person(const Person& p)"。接着,调用派生类Student的拷贝构造函数,将s2的_num初始化为s1的_num,然后打印"Student(const Student& s)"。 -
Person p = s1;
:将s1赋值给一个Person对象p。这里会调用基类Person的拷贝构造函数,将p的_name初始化为s1的_name,然后打印"Person(const Person& p)"。注意,这里只会复制基类部分,派生类Student的成员_num不会被复制。 -
s1 = s2;
:将s2赋值给s1。首先,调用基类Person的赋值运算符重载函数,将s1的_name设置为s2的_name,然后打印"Person operator=(const Person& p)"。接着,调用派生类Student的赋值运算符重载函数,将s1的_num设置为s2的_num,然后打印"Student& operator=(const Student& s)"。 -
return 0;
:main函数结束,返回0。在main函数结束时,s1、s2和p的析构函数会被自动调用。首先,调用基类Person的析构函数,打印"~Person()",这是对p的析构。然后,调用派生类Student的析构函数,打印"Student()",接着,自动调用基类Person的析构函数,然后打印"Person()"。这个过程会对s1和s2重复两次。
五、继承与友元
class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{//添加下面这句就可以解决//friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 学号
};void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}

六、继承与静态成员
class Person
{
public:Person() { ++_count; }
//protected:string _name; // 姓名
public:static int _count; // 统计人的个数。
};
int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};class Graduate : public Student
{
protected:string _seminarCourse;
};int main()
{Person p;Student s;//地址不同cout << &(p._name) << endl;cout << &(s._name) << endl;//地址相同cout << &(p._count) << endl;cout << &(s._count) << endl;//计算创建的对象的个数Graduate g1;Graduate g2;cout << Person::_count << endl;cout << Graduate::_count << endl;return 0;
}
Student类继承自Person类,Graduate类继承自Student类。
-
Person
类:这个类有两个成员变量,一个是_name
,类型为string
,表示人的姓名;另一个是_count
,类型为int
,是一个静态成员变量,用于统计Person类的对象个数。在Person类的构造函数中,每次创建一个新的Person对象时,_count
就会加1。 -
Student
类:这个类继承自Person类,增加了一个成员变量_stuNum
,类型为int
,表示学生的学号。 -
Graduate
类:这个类继承自Student类,增加了一个成员变量_seminarCourse
,类型为string
,表示研究生的研讨课程。
在main
函数中,首先创建了一个Person对象p
和一个Student对象s
。然后,通过cout
语句输出了p
和s
的_name
成员变量的地址。由于_name
是非静态成员变量,每个对象都有自己的_name
,所以p
和s
的_name
的地址是不同的。
接着,通过cout
语句输出了p
和s
的_count
成员变量的地址。由于_count
是静态成员变量,所有的对象共享同一个_count
,所以p
和s
的_count
的地址是相同的。
然后,创建了两个Graduate对象g1
和g2
。由于Graduate类继承自Person类,所以每创建一个Graduate对象,Person类的_count
都会加1。
最后,通过cout
语句输出了Person类和Graduate类的_count
。由于Graduate类继承自Person类,所以Graduate类的_count
和Person类的_count
是相同的。
七、复杂的菱形继承及菱形虚拟继承


1、菱形继承

class Person
{
public:string _name; // 姓名
};
class Student : virtual public Person
{
protected:int _num; //学号
};
class Teacher : virtual public Person
{
protected:int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};void Test()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;a._name = "peter";// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}
2、虚拟继承
class A {
public:int _a;
};class B : virtual public A {
public:int _b;
};class C : virtual public A {
public:int _c;
};class D : public B, public C {
public:int _d;
};int main() {D d;d._a = 1; // 通过B或C访问_a时,需要使用作用域解析运算符d._b = 3;d._c = 4;d._d = 5;return 0;
}
下图是菱形继承的内存对象成员模型:这里可以看到数据冗余
下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下 面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?
3、例题
下面代码的调用顺序?
#include <iostream>
using namespace std;class A {
public:A(char *s) { cout << s << endl; }~A() {}
};class B : virtual public A {
public:B(char *s1, char *s2) : A(s1) {cout << s2 << endl;}
};class C : virtual public A {
public:C(char *s1, char *s2) : A(s1) {cout << s2 << endl;}
};class D : public B, public C {
public:D(char *s1, char *s2, char *s3, char *s4) : B(s1, s2), C(s1, s3), A(s1) {cout << s4 << endl;}
};int main() {D *p = new D("class A", "class B", "class C", "class D");delete p;return 0;
}
在D类的构造函数中,分别调用了B和C的构造函数,而B和C的构造函数又分别调用了A的构造函数。因此,构造函数的调用顺序是A -> B -> C -> D。
八、继承的总结和反思
- public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
- 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
- 优先使用对象组合,而不是类继承 。
- 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称 为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的 内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很 大的影响。派生类和基类间的依赖关系很强,耦合度高。
- 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象 来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复 用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被 封装。
- 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用 继承,可以用组合,就用组合。
class Car {
protected:string _colour = "白色"; // 颜色string _num = "陕ABIT00"; // 车牌号
};class BMW : public Car {
public:void Drive() { cout << "好开-操控" << endl; }
};class Benz : public Car {
public:void Drive() { cout << "好坐-舒适" << endl; }
};class Tire {
protected:string _brand = "Michelin"; // 品牌size_t _size = 17; // 尺寸
};class CarWithTire : public Car {
protected:Tire _t; // 轮胎
};
相关文章:

C++ 继承
目录 一、继承的概念及定义 1、继承的概念 2、继承定义 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承及菱形虚拟继承 1、菱形继承 2、虚拟继承 3、例题 八、继承的总结和反思…...

了解ASP.NET Core 中的文件提供程序
写在前面 ASP.NET Core 通过文件提供程序来抽象化文件系统访问。分为物理文件提供程序(PhysicalFileProvider)和清单嵌入的文件提供程序(ManifestEmbeddedFileProvider)还有复合文件提供程序(CompositeFileProvider );其中PhysicalFileProvider 提供对物理文件系统…...

竞赛保研 基于深度学习的人脸性别年龄识别 - 图像识别 opencv
文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 毕业设计…...
JavaScript音视频,JavaScript简单获取电脑摄像头画面并播放
前言 本章实现JavaScript简单获取电脑摄像头画面并播放的功能 兼容性(不支持Node.js) 需要注意的是,由于涉及到用户的隐私和安全,获取用户媒体设备需要用户的明确同意,并且可能需要在用户的浏览器中启用相关的权限。在某些浏览器中,可能需要用户手动开启摄像头权限。 …...
《JVM由浅入深学习【五】 2024-01-08》JVM由简入深学习提升分享
目录 JVM何时会发生堆内存溢出?1. 堆内存溢出的定义2. 内存泄漏的原因3. 堆内存溢出的常见场景4. JVM参数调优5. 实际案例分析 JVM如何判断对象可以回收1.可达性分析的基本思路2.实际案例3.可以被回收的对象4.拓展, 谈谈 Java 中不同的引用类型? 结语感…...

FastDFS之快速入门、上手
知识概念 分布式文件系统 通过计算机网络将各个物理存储资源连接起来。通过分布式文件系统,将网络上任意资源以逻辑上的树形结构展现,让用户访问网络上的共享文件更见简便。 文件存储的变迁: 直连存储:直接连接与存储…...

Vue 中的 ref 与 reactive:让你的应用更具响应性(中)
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...

【数据库基础】Mysql与Redis的区别
看到一篇不错的关于“Mysql与Redis的区别”的文章,转过来记录下~ 文章目录 一、数据库类型二、运行机制三、什么是缓存数据库呢?四、优缺点比较五、区别总结六、数据可以全部直接用Redis储存吗?参考资料 一、数据库类型 Redis:NOS…...

JVM工作原理与实战(六):类的生命周期-连接阶段
专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、类的生命周期 1.加载(Loading) 2.连接(Linking) 3.初始化(Initialization) 4.使用(Using&…...
【OCR】 - Tesseract OCR在Windows系统中安装
Tesseract OCR 在Windows环境下安装Tesseract OCR(Optical Character Recognition)通常包括以下几个步骤: 下载Tesseract 访问Tesseract的GitHub发布页面:https://github.com/tesseract-ocr/tesseract/releases找到适合你操作系…...

YOLOv8改进 | 损失函数篇 | SlideLoss、FocalLoss分类损失函数助力细节涨点(全网最全)
一、本文介绍 本文给大家带来的是分类损失 SlideLoss、VFLoss、FocalLoss损失函数,我们之前看那的那些IoU都是边界框回归损失,和本文的修改内容并不冲突,所以大家可以知道损失函数分为两种一种是分类损失另一种是边界框回归损失,上一篇文章里面我们总结了过去百分之九十的…...
计算机网络试题——填空题(附答案)
在OSI模型中,第一层是____________层。 答案:物理(Physical) TCP协议是一种_____________连接的协议。 答案:面向连接(Connection-oriented) IPv6地址的位数是____________。 答案:1…...

第二证券:股票私募仓位指数创近八周新高
1月8日,A股几大首要指数全线收跌,上证指数收于日内最低点2887.54点,间隔上一年5月份的阶段高点3418.95点现已跌去了15.54%。 不过,虽然商场仍未清晰止跌,私募基金们却现已进场“抄底”。私募排排网最新发布的私募仓位…...
35-javascript基础,引入方式;变量命名规范
html分为三部分;结构html,表现css,行为js;js就是javascript js包含三部分: ECMAScript:简称ES,ES5,ES6核心语法 DOM:获取和操作html元素的标准方法;BOM&am…...

笔试案例2
文章目录 1、笔试案例22、思维导图 1、笔试案例2 09)查询学过「张三」老师授课的同学的信息 selects.*,c.cname,t.tname,sc.score from t_mysql_teacher t, t_mysql_course c, t_mysql_student s, t_mysql_score sc where t.tidc.cid and c.cidsc.cid and sc.sids…...
【嵌入式-网络编程】vmware中使用UDP广播失败问题
问题描述: 自己在vmware中搭建了2台虚拟机,虚拟机A向虚拟机A和虚拟机B发送广播信息,接收端在虚拟机A和虚拟机B,这个时候,由于没配置sin.sin_addr.s_addr htonl(INADDR_ANY);,而是配置的inet_pton(AF_INET,…...

2020年认证杯SPSSPRO杯数学建模D题(第二阶段)让电脑桌面飞起来全过程文档及程序
2020年认证杯SPSSPRO杯数学建模 D题 让电脑桌面飞起来 原题再现: 对于一些必须每天使用电脑工作的白领来说,电脑桌面有着非常特殊的意义,通常一些频繁使用或者比较重要的图标会一直保留在桌面上,但是随着时间的推移,…...

vue3 修饰符大全(近万字长文)
系列文章目录 TypeScript 从入门到进阶专栏 文章目录 系列文章目录前言一、事件修饰符(Event Modifiers)1、.stop(阻止事件冒泡)2、.prevent(阻止事件的默认行为)3、.capture(使用事件捕获模式…...

HarmonyOS@State装饰器:组件内状态
State装饰器:组件内状态 State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。 在状态变量相关装饰器中,State是最基础的&…...

如何让GPT支持中文
上一篇已经讲解了如何构建自己的私人GPT,这一篇主要讲如何让GPT支持中文。 privateGPT 本地部署目前只支持基于llama.cpp 的 gguf格式模型,GGUF 是 llama.cpp 团队于 2023 年 8 月 21 日推出的一种新格式。它是 GGML 的替代品,llama.cpp 不再…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
从零手写Java版本的LSM Tree (一):LSM Tree 概述
🔥 推荐一个高质量的Java LSM Tree开源项目! https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree,专为高并发写入场景设计。 核心亮点: ⚡ 极致性能:写入速度超…...

基于Python的气象数据分析及可视化研究
目录 一.🦁前言二.🦁开源代码与组件使用情况说明三.🦁核心功能1. ✅算法设计2. ✅PyEcharts库3. ✅Flask框架4. ✅爬虫5. ✅部署项目 四.🦁演示效果1. 管理员模块1.1 用户管理 2. 用户模块2.1 登录系统2.2 查看实时数据2.3 查看天…...

Android Framework预装traceroute执行文件到system/bin下
文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数(使用 ICMP Echo 请求)-T 参数(使用 TCP SYN 包) 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11,在/s…...