【C++】非常重要的——多态

凡是面向对象的语言,都有三大特性,继承,封装和多态,但并不是只有这三个特性,是因为者三个特性是最重要的特性,那今天我们一起来看多态!
目录
1.多态的概念
1.1虚函数
1.2虚函数的重写
1.3虚函数重写的两个例外
1.子类的虚函数可以不加virtual
2. 协变(基类与派生类虚函数返回值类型不同)
1.4如何实现一个不能被继承的类
2. 多态的定义及实现
2.1多态调用
2.2普通调用:
2.3析构函数建议加virtual吗?
2.4抽象类
2.5接口继承和实现继承
3.多态的实现原理
3.1.虚表(虚函数表)
3.2多态的实现
3.3静态多态和动态多态
3.4单继承的多态实现
3.5多继承的多态实现
4.一些常考的多态的问题
总结:
1.多态的概念
多态,就是不同对象去完成某一种行为时,产生的不同状态。
举例说明:日常生活中,我们去买票,尤其买火车票时,总会有不同的结果。当成人买的时候,就是原价,学生就是半价,军人就是优先买票,这都体现了多态。不同对象完成某一行为,产生不同状态。
那实现多态前,我们首先得清楚一些概念:
1.1虚函数
class A
{
public:virtual void func(){cout << "A" << endl;}
}; 1.2虚函数的重写
我们之前在继承的时候,学习过重定义(隐藏),现在在多态这一节,又出现了重写,那到底是什么呢?我们来看一看
重定义(隐藏):首先在两个不同的类中(父类,子类)(构成继承),只要函数名相同,就会构成重定义(隐藏)。
重写(覆盖):在重定义的基础上,除了函数名要相同,还有返回值,参数都得相同,这才构成重写。
举个例子:
class A
{
public:virtual void func(){cout << "A" << endl;}
};
class B:public A
{
public:virtual void func() //重写{cout << "B" << endl;}
}; 总结就是:虚函数的重写条件:子类和父类都是虚函数,且函数名,返回值,参数都必须相同(三同),这才能构成虚函数的重写。
1.3虚函数重写的两个例外
1.子类的虚函数可以不加virtual
因为是继承关系,父类的虚函数也被继承下来,所以子类的可以不加virtual。(建议还是都写上)
class A
{
public:virtual void func(){cout << "A" << endl;}
};
class B:public A
{
public:void func(){cout << "B" << endl;}
}; 2. 协变(基类与派生类虚函数返回值类型不同)
意思是:三同中,返回值可以不同,但要求返回值必须是父子类关系的指针或引用。(其他父子类关系的指针或者引用也可以)
class person
{
public:virtual person* func() //本父子类指针或引用{return this;}
};class student:public person
{
public:virtual student* func() //本父子类指针或引用{return this;}
}; 其他父子类关系的指针或者引用也可以:
class A
{};
class B:public A
{};class person
{
public:virtual A* func(){return nullptr;}
};class student:public person
{
public:virtual A* func(){return nullptr;}
}; 但是父类的返回值不可以为子类的指针。
1.4如何实现一个不能被继承的类
方法一:是需要把它的构造函数写为私有即可,无法构造,就不可能被继承;
方法二:类定义时,加final(c++11),最终类,不能被继承
class A final {}; class B:public A {};若final给虚函数,虚函数则不能被重写
class A {virtual void func()final{} }; class B:public A {virtual void func(){} };
override是来判断是否已经重写(检查重写)
class A {virtual void func(int){} }; class B:public A {virtual void func()override{} };
2. 多态的定义及实现
首先多态实现的前提必须是继承!
多态实现的两个条件:
1.必须使用父类(基类)的指针或者引用调用虚函数;
2.被调用的函数必须是虚函数,且子类(派生类)必须对虚函数进行重写;
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。2.1多态调用
class Person { public:virtual void Buyticket(){cout << "Person:全价" << endl;} };class Student:public Person { public:virtual void Buyticket(){cout << "Student:半价" << endl;} };void func(Person& p) //切片 {p.Buyticket(); }int main() {Person p;Student s;func(p);func(s); }
2.2普通调用:
不符合多态条件即可:
void func(Person p)//不是指针或者引用,就是对象 {p.Buyticket(); }int main() {Person p;Student s;func(p);func(s); }
那么我们可以发现:
普通调用跟调用对象的类型有关;
多态调用必须是父类的指针或者引用,无论是是哪个对象传,他都会指向该对象中父类的那一部分(切片),进而调用该对象中的虚函数。一句话,多态调用跟 指针/引用 指向的对象有关
2.3析构函数建议加virtual吗?
我们看一个例子:
class Person { public:~Person(){cout << "Person delete" << endl;delete _p;} protected:int* _p = new int[10]; };class Student :public Person { public:~Student(){cout << "Student delete" << endl;delete _s;} protected:int* _s = new int[20]; };int main() {//Person p;//Student s;Person* ptr1 = new Person;Person* ptr2 = new Student;delete ptr1;delete ptr2; }我们都知道,析构函数自动调用,在继承中,子类会先析构,调用子类的析构函数以后,自动再调用父类的析构函数。
但这用情况还适用吗?
先看一下结果:
我们发现,居然调用了两次父类的析构函数 !!!
这种情况就会造成子类对象中的成员变量没有释放,导致内存泄露!!
我们知道:
delete有两种行为:1.使用指针调用析构函数;2.operator delete(ptr)
所以使用指针调用析构函数是普通调用(不满足多态调用的条件),普通调用是跟调用的对象类型有关,类型都是Person,所以只会调用person的析构函数
但此时我们更希望的是多态调用,所以建议加virtual,指针指向的对象是哪个,就调用哪个的析构函数。但此时我们会想,析构函数名字都不一样,这能构成重写吗?当然可以,那是因为编译器会自动把父类子类的析构函数名字换成一样的:ptr->destructor()。
那么就可以实现我们预期的效果:
所以我们建议:再写析构函数时,可以无脑给父类的析构函数加virtual,防止出现上面的情况,导致内存泄露 。
普通调用时,时普通调用;父类的指针或者引用调用时,时多态调用,互不影响!
2.4抽象类
class Car
{
public:virtual void Drive() = 0;
};class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};
void Test()
{Car* pBMW = new BMW;pBMW->Drive();
}int main()
{Test();
} 总结:有些类不需要类的对象,可以在写成纯虚函数。
2.5接口继承和实现继承
接口继承针对虚函数;实现继承针对普通函数。
class A
{
public:virtual void func(int val = 1){ std::cout << "A->" << val << std::endl; }virtual void test(){ func(); }
};class B : public A
{
public:void func(int val = 0){ std::cout << "B->" << val << std::endl;
};int main(int argc, char* argv[])
{B*p = new B;p->test(); return 0;
}A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确若是ptr->func(),就是B类对象直接调用,就是普通调用,普通调用跟对象类型有关。
普通调用在编译时就会静态绑定,在编译时调用的函数以及函数的默认值就已经确定,子类调用子类自己的函数,跟父类没有任何关系,函数都是子类编译时就已经静态绑定的,所以缺省值依然是0。最终结果是B->0 答案选哪个??
首先我们了解的第一点是,继承父类的成员,会原封不动的继承到子类;
我们接下来看:创建了一个B对象的指针,指针来调用p->test(),这时候,会直接调用父类中的test,再this->func(),此时的this的类型是A*,因为test处于A类中,继承到B中,也会原封不动的继承过去,this依然是A*,所以父类的指针调用虚函数,满足多态的调用,多态调用是看指针指向的对象,又因为p调用的test,所以指针指向B对象,所以会调用B的重写的func虚函数,所以最终答案是B->1.(其实多态调用一直是调的父类的接口,再根据指向的对象去调用具体的实现,后面会详细讲到)
当B对象自己调用函数func时,当不是多态调用时,就会直接调用自己的func(),缺省值还是自己的val=0.
3.多态的实现原理
3.1.虚表(虚函数表)
来先看一道题:
class Base1
{
public:virtual void Func1(){cout << "Func1()" << endl;}};class Base2
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
sizeof(Base1),sizeof(Base2),它所占的字节数是多少? 通过之前学习的内容,我们可以了解到,如果类中没有成员变量,只有成员函数,会留一个字节进行占位,因为成员函数在代码段,所以Base1的大小是1吗?

原来不是我们想象的那样子,是事实上,来看:

凡是有虚函数的,都会有一个虚函数表指针来存虚函数,简称虚表指针,存虚函数的表叫做虚函数表,简称虚表。
VFptr(全程vftable)是一个指针, 指向虚表,虚表中存的是虚函数的地址。
所以我们知道,原来只要有虚函数,就会有虚表指针,所以Base1的字节大小是,4字节;
Base2的字节大小是,加上内存对齐,_b占四字节,vtf占四字节,8字节。

对于同一类实例化出的不同的对象,他们的虚表是公用的:
class A
{public:virtual void func(){}
}int main()
{A b;A c;
} 
我们了解虚表和虚表指针以后,那么多态到底如何实现呢?
3.2多态的实现
来看一段代码:
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;char _ch;
};class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}void Func3(){cout << "Derive::Func3()" << endl;}
private:int _d = 2;
};int main()
{Base b;Derive d;//普通调用Base* ptr = &b;ptr->Func3();ptr = &d;ptr->Func3();
//多态调用ptr = &b;ptr->Func1();ptr = &d;ptr->Func1();
} 
多态调用:
ptr是父类的指针,无论指向哪个对象都只能看到该对象父类的部分(切片),那么多态调用怎么调用呢?通过虚表指针来调用虚函数,完成重写的虚函数会在虚表对应的位置进行覆盖,变成重写后的虚函数,进而调用。(一句话,我也不知道我调用谁,我指向谁,就调用谁的虚函数,进而完成动态绑定,完成多态调用)
静态绑定:编译时,通过类型就确定调用函数的地址,然后直接call完成调用
通过反汇编可以看到:

静态绑定,一步完成;动态绑定得很多步完成。
3.3静态多态和动态多态
总结:多态调用就是依靠虚表实现,指向谁,就调用谁的虚函数
虚表是存在代码段中的。
3.4单继承的多态实现
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }void func4() { cout << "Derive::func4" << endl; }
private:int b;
};int main()
{Base b;Derive d;} 我们知道Base对象中的虚表有func1和func2,子类对虚函数进行重写,func1重写,func2不变:
那么子类自己的虚函数func3在不在虚表里面呢?
为了更方便观察,我们可以实现一个打印虚表的函数:
typedef void(*VFTptr)(); //函数指针,重命名必须写到里面
void Print(VFTptr VFT[]) //函数指针数组
{int i = 0;while (VFT[i]) //虚表中,vs默认以空结束。{printf("[%d]%p->", i, VFT[i]);VFT[i]();i++;}cout << endl;
}
int main()
{Base b;Derive d;Print((VFTptr*)(*(int*)&b)); //先取地址,再强转VFTptr的地址,然后解引用取到地址,再强转为VFT*类型,进而传参调用Print((VFTptr*)(*(void**)&d));//换为void**原因是因为,机器若是32位,指针大小就是4字节,若是64位,就是8字节//所以换为void**更普适,先取地址,再强转void**,void*解引用,那么这就根据机器的位数来决定指针的大小了
} 
我们可以发现,虚函数func3也会存在虚表中。
3.5多继承的多态实现
typedef void(*VFTptr)();
void Print(VFTptr VFT[])
{int i = 0;while (VFT[i]){printf("[%d]%p->", i, VFT[i]);VFT[i]();i++;}cout << endl;
}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; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};int main()
{Base1 b1;Base2 b2;Print((VFTptr*)(*(void**)&b1));Print((VFTptr*)(*(void**)&b2));Derive d;Print((VFTptr*)(*(void**)&d));//Base1的虚表Print((VFTptr*)(*(void**)((char*)&d + sizeof(Base1))));//Base2的虚表//Base2* ptr=&d;//Print((VFTptr*)(*(void**)ptr)); //也可以这样找到虚表指针} 
我们知道多继承下多态的实现,子类继承多个父类,只有当父类有虚函数,多继承时才有虚表。
当子类也有虚函数时,这时子类的虚函数放到第一个继承的父类的虚表中,我们可以从上面代码结果看出。
再来练习题目:
下列输出的结果是什么?
class A{
public:A(char *s) { cout << s << endl; }~A(){}
};class B :public A
{
public:B(char *s1, char*s2) :A(s1) { cout << s2 << endl; }
};class C : public A
{
public:C(char *s1, char*s2) :A(s1) { cout << s2 << endl; }
};class D :public C, public B
{
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;
}
A:class A class B class C class D
B:class D class B class C class A
C:class D class C class B class A
D:class A class C class B class D 答案是哪个呢?
首先D肯定是最后一个才被初始化的,构造函数先走初始化列表,B,C,A,那肯定是A先被初始化,因为B,C中都有A,A不初始化,B,C没办法初始化;其次要看继承的顺序,D先继承C,再继承B,所以先初始化C,再初始化B.最终答案就是D
第二题:
class Base1 {public: int _b1;};
class Base2 { public: int _b2; };
class Derive : public Base2, public Base1
{ public: int _d; };int main(){Derive d;Base1* p1 = &d;Base2* p2 = &d;Derive* p3 = &d;return 0;
}
A:p1 == p2 == p3 B:p1 > p2 == p3 C:p1 == p3 != p2 D:p1 != p2 != p3 答案选哪个呢?
子类首先继承了Base2,再继承了Base1,所以模型应该是这样的:

所以没有答案,答案应该是:p3==p2<p1.
所以通过上面这两个例子,我们可以看的出,其实实现继承时,继承的顺序是非常重要的,有关谁先被创建。
4.一些常考的多态的问题
总结:
这一节,我们完完整整把多态的全部内容都讲了一遍,当然途中大家肯有会有不懂的地方,因为这是难点,我在编写这边文章的时候,也是反反复复思考和学习,所以大家需要反复思考观看,不懂得可以在评论区回复,或者私信我哦!
大家加油!!!
相关文章:
【C++】非常重要的——多态
凡是面向对象的语言,都有三大特性,继承,封装和多态,但并不是只有这三个特性,是因为者三个特性是最重要的特性,那今天我们一起来看多态! 目录 1.多态的概念 1.1虚函数 1.2虚函数的重写 1.3虚…...
发票账单很多?python助你批量完成数据提取
每天面对成堆的发票,无论是税务发票还是承兑单据,抑或是其他各类公司数据要从照片、PDF等不同格式的内容中提取,我们都有必要进行快速办公的能力提升。因此,我们的目标要求就十分明显了,首先要从图片中获取数据&#x…...
[闪存2.1] NAND FLASH特性串烧 | 不了解闪存特性,你能用好闪存产品吗?
前言 为了利用好闪存, 发挥闪存的优势, 以达到更好的性能和使用寿命, 那自然要求了解闪存特性。 闪存作为一种相对较新的存储介质, 有很多特别的特性。 一.闪存的特性 凡是采用Flash Memory的存储设备,可以统称为闪存存储。我们经常谈的固态硬盘(SSD),可以由volatile/…...
面试官问我按钮级别权限怎么控制,我说v-if,面试官说再见
最近的面试中有一个面试官问我按钮级别的权限怎么控制,我说直接v-if啊,他说不够好,我说我们项目中按钮级别的权限控制情况不多,所以v-if就够了,他说不够通用,最后他对我的评价是做过很多东西,但…...
阿里云服务器使用教程:CentOS 7安装nginx详细步骤
目录 1、下载nginx压缩包 2、配置nginx安装所需环境 3、解压nginx压缩包 4、编译安装nginx 5、nginx启动...
Android JNI浅析、Java和Native通信对象的传值和回调
简单了解一下jni JNI是一个本地编程接口,它允许运行在Java虚拟机的Java代码与用其他语言(如C,C和汇编)编写的库交互。 jni函数签名 首先看一下java类型对应的jni类型: Java类型符号BooleanZByteBCharCShortSIntILongJFloatFDo…...
linux目录/usr/lib/systemd/system目录详解
文章目录前言一. systemd介绍二. service 脚本详解2.1 [Unit] 区块2.2 [Service] 区块2.3 [Install] 区块总结前言 init的进化经历了这么几个阶段: CentOS 5: SysV init,串行 CentOS 6:Upstart,并行,借鉴ubuntu CentOS 7:Syste…...
408考研计算机之计算机组成与设计——知识点及其做题经验篇目4:CPU的功能和基本结构
随着考研的慢慢复习,我们逐渐进入了计算机组成与设计的第五章中央处理器。它原名为CPU。姓C,名PU,字中央处理器,号计组难点,乃计算机之中心与核心部件,小编称之曰能算能控,赐名曰九天宏教普济生…...
2022-12-10青少年软件编程(C语言)等级考试试卷(五级)解析
2022-12-10青少年软件编程(C语言)等级考试试卷(五级)解析T1、漫漫回国路 2020年5月,国际航班机票难求。一位在美国华盛顿的中国留学生,因为一些原因必须在本周内回到北京。现在已知各个机场之间的航班情况,求问他回不回得来(不考虑转机次数和机票价格)。 时间限制:10…...
刷题专练之链表(一)
文章目录前言一、 移除链表元素1.题目介绍2.思路3.代码二、反转链表1.题目介绍2.思路3.代码三、链表的中间结点1.题目介绍2.思路3.代码四、链表的中间结点1.题目介绍2.思路3.代码前言 以下是链表经常考的面试题,我在这里进行归纳和讲解,采取的是循序渐进…...
elasticsearch高级查询api
yml配置 #es配置 spring:elasticsearch:rest:uris: 192.168.16.188:9200添加依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency>使用编程的形式…...
力扣-股票的资本损益
大家好,我是空空star,本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目:1393. 股票的资本损益二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他…...
蓝桥杯刷题冲刺 | 倒计时26天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.路径2.特别数的和3.MP3储存4.求和1.路径 题目 链接: 路径 - 蓝桥云课 (lanqiao.cn…...
嵌入式软件开发之Linux 用户权限管理
目录 Ubuntu 用户系统 权限管理 权限管理命令 权限修改命令 chmod 文件归属者修改命令 chown Ubuntu 用户系统 Ubuntu 是一个多用户系统,我们可以给不同的使用者创建不同的用户账号,每个用户使用各自的账号登陆,使用用户账号的目的一是方便…...
2023-03-15 RabbitMQ
RabbitMQ整合 官网erlang版本 : 20.3.8.x 官方rabbitMq版本: rabbitmq-server-generic-unix-3.7.14.tar.xz 1.安装 1.1 安装erlang 1.安装环境 yum -y install make gcc gcc-c kernel-devel m4 ncurses-devel openssl-devel2.在/usr/local/下创建erlangapp文件…...
二叉树链式结构的实现
文章目录1.二叉树的遍历1.1前序、中序以及后序遍历1.2代码测试1.3层序遍历1.4二叉树遍历习题2.节点个数以及高度2.1二叉树节点个数2.2叶子节点个树2.3第k层节点个数2.4树的高度1.二叉树的遍历 1.1前序、中序以及后序遍历 学习二叉树结构,最简单的方式就是遍历。所…...
蓝桥杯刷题冲刺 | 倒计时28天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.卡片2.数字三角形3.购物单4.回文日期1.卡片 题目 链接: 卡片 - 蓝桥云课 (lanqiao…...
一文带你吃透操作系统
文章目录1. 进程、线程管理2. 内存管理3. 进程调度算法4. 磁盘调度算法5. 页面置换算法6. 网络系统7. 锁8. 操作系统知识点文章字数大约1.9万字,阅读大概需要65分钟,建议收藏后慢慢阅读!!!1. 进程、线程管理 进程和线程…...
计算机网络英文简称汇总
分类名词全拼汉译概述B2CBusiness-to-Consumer商对客概述P2PPeer-to-Peer对等概述C/SClient-Server服务器-客户机概述ITUInternational Telecommunication Union国际电信联盟概述IEEEInstitute of Electrical and Electronics Engineers电气与电子工程师协会概述ICCCInternatio…...
腾讯云云服务器标准型S5性能配置简单测评
腾讯云服务器标准型S5实例CPU采用Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器,主频2.5GHz,睿频3.1GHz,标准型S5云服务器基于全新优化虚拟化平台,配有全新的Intel Advanced Vector Extension (AVX-512) 指令集&#…...
Musa并行搜索工具:重塑信息检索工作流,提升多源对比效率
1. 项目概述:重新定义你的搜索工作流如果你和我一样,每天的工作都离不开在浏览器里反复横跳——为了一个技术问题,先在 Google 搜一遍,再去 Stack Overflow 看看有没有新答案,接着打开 ChatGPT 问问它的看法࿰…...
SEAforth多核芯片在工业控制中的并行处理优势
1. SEAforth芯片架构解析:工业控制的并行革命在工业自动化领域,传统单核MCU正面临越来越严峻的性能瓶颈。我曾参与过一个大型石化厂的温度监测系统改造项目,原系统采用常规ARM处理器,当需要同时处理32路热电偶信号、4路压力传感器…...
Arduino Uno R3 bootloader烧写避坑全记录:从USBasp驱动安装到熔丝位设置(Win10/11实测)
Arduino Uno R3 bootloader烧写实战指南:从驱动配置到熔丝位安全操作 当一块全新的Atmega328P芯片静静躺在工作台上时,它就像一张白纸,等待着被赋予生命。作为硬件开发者,我们常常需要为这些空白芯片注入灵魂——烧写bootloader。…...
基于Vue 3与Express的私有化ChatGPT Web客户端部署指南
1. 项目概述与核心价值最近在折腾一个自用的AI对话工具,核心需求很简单:想在一个自己完全掌控的界面上,方便地使用大语言模型,比如ChatGPT的API。市面上虽然有很多现成的网页应用,但要么功能太臃肿,要么部署…...
DeepSeek API Gateway与大模型推理服务深度协同:如何实现Token级流控、异步响应封装、Streaming SSE自动保活?
更多请点击: https://intelliparadigm.com 第一章:DeepSeek API Gateway架构全景与核心定位 DeepSeek API Gateway 是面向大模型服务的高性能、可扩展网关系统,承担请求路由、认证鉴权、流量控制、协议转换与可观测性聚合等关键职责。它并非…...
Vivado时序约束实战:输入/输出延时设置背后的时序模型与设计考量
1. 时序约束的本质:从理论到实践的桥梁 刚接触FPGA设计时,我最头疼的就是时序约束。那些建立时间、保持时间的概念看得人云里雾里,更别说要在Vivado里实际设置了。直到有一次项目因为时序问题导致整板无法工作,我才真正明白时序约…...
HTML5 教程
HTML5 教程 引言 HTML5,作为现代网页开发的核心技术之一,自从推出以来就受到了广泛的关注和认可。它不仅改进了原有的HTML特性,还引入了新的元素和API,使得网页设计和开发更加高效、强大。本教程旨在为您提供一个全面的HTML5学习路径,帮助您快速掌握HTML5的基础知识和高…...
别再让专利证书变废纸!手把手教你用6步法写出能维权的权利要求书
从技术到法律:6步打造高价值专利权利要求的实战指南 刚拿到专利证书的工程师小王,在展会上发现竞争对手的产品几乎照搬了自己的发明。他信心满满地提起诉讼,却因权利要求书中"数据传输模块"的表述过于宽泛而败诉——法院认为该描述…...
如何在5分钟内将你的普通鼠标变成macOS生产力神器
如何在5分钟内将你的普通鼠标变成macOS生产力神器 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix 还在为macOS上鼠标滚轮生硬、侧键闲置而烦恼吗…...
Python全栈实战:前后端分离开发核心要点
后端API搭建FastAPI与Flask是Python全栈开发的主流后端框架选择。两者均支持RESTful API开发,但适用场景不同:FastAPI代码示例(高性能方案):from fastapi import FastAPI app FastAPI()app.get("/items/{item_id…...





