多态的学习

1. 🏷多态的概念

多态的概念: 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态。
举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人
买票时是优先买票。
再举个栗子: 最近为了争夺在线支付市场,支付宝年底经常会做诱人的扫红包-支付-给奖励金的
活动。那么大家想想为什么有人扫的红包又大又新鲜8块、10块…,而有人扫的红包都是1毛,5
毛…。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据,比如你是新用户、比如
你没有经常支付宝支付等等,那么你需要被鼓励使用支付宝,那么就你扫码金额 =random()%99;比如你经常使用支付宝支付或者支付宝账户中常年没钱,那么就不需要太鼓励你去使用支付宝,那么就你扫码金额 = random()%1;总结一下:同样是扫码动作,不同的用户扫得到的不一样的红包,这也是一种多态行为。ps:支付宝红包问题纯属瞎编,大家仅供娱乐


2. 🏷多态的定义及实现

📌虚函数

虚函数: 即被 virtual 修饰的类成员函数称为虚函数

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

📌虚函数的重写

✒概念:虚函数的重写(覆盖):

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型函数名字、*参数列表完全相同),称子类的虚函数重写了基类的虚函数

✒如何重写?

  1. 首先得是一个虚函数:使用关键字virtual 来修饰成员函数 让其成为一个虚函数
  2. 然后安照:返回值类型函数名字、*参数列表完全相同来写子类的成员函数
class Person {   //父类public:virtual void BuyTicket() { cout << "买票-全价" << endl; }};class Student : public Person {  //子类public:virtual void BuyTicket() { cout << "买票-半价" << endl; }/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用*//*void BuyTicket() { cout << "买票-半价" << endl; }*/};void Func(Person& p)  //必须是父类的指针或者引用
{ p.BuyTicket(); }int main(){Person ps;Student st;Func(ps);Func(st);return 0;}

✒虚函数重写的两个例外:

  1. 协变(基类与派生类虚函数返回值类型不同)
    派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。(了解)
class A{}; //父类class B : public A {}; //子类class Person 
{public:virtual A* f() {return new A;} //返回父类的指针
};class Student : public Person
{ public:virtual B* f() {return new B;}[ //返回子类的指针]
};
  1. 析构函数的重写(基类与派生类析构函数的名字不同)
    如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
class Person 
{public:virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person 
{public:virtual ~Student() { cout << "~Student()" << endl; }
};// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

📌多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。

那么在继承中要构成多态还有两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
    在这里插入图片描述

📌多态的一些理解

✒多态的举例:

#include<iostream>
using namespace std;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 Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}

✒ 不继承

上面的代码如果我们 不继承 的话,效果会怎样?

#include<iostream>
using namespace std;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 Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}

改了之后上面的代码会报错,因为,你Func函数的参数是person 类型的,但是你的Johosonstudent类型 的,所以类型不匹配,报错。

✒不引用

如果我们把Func函数改变一下:

它原来是这样的:

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

如果它的参数,不是指针也不是引用的话,那将怎样呢?
如下:

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

修改后的完整代码如下:

#include<iostream>
using namespace std;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 Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}

在这里插入图片描述

由于它不是指针也不是引用,说明他不满足多态的定义,所以这里就是普通的函数调用。

这里提一下 普通调用和多态调用的区别
在这里插入图片描述

✒把引用换成指针


#include<iostream>
using namespace std;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 Mike;Func(&Mike);   // 所以传参也会反生改变Student Johnson;Func(&Johnson); //注意这里也会发生变化return 0;
}

✒判断下面的情况是否构成多态

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

这里把父类的virtual 删除掉了

如果你毫无思绪我们这里可以提一下多态的条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

答:这样不构成多态,因为父类的不是virtual 没有完成对虚函数的重写

那下面这种情况构成多态吗?

class Person { //父类
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person //子类
{
public:void BuyTicket() { cout << "买票-半价" << endl; }
};```这里把子类里的``virtual`` 删掉了答: 这里构成多态,这里就是上面我们写的虚函数重写的两个例外;### ✒在什么场景下析构函数必须是 虚函数?在下面的场景是不需要析构函数是虚函数的
```c++
#include<iostream>
using namespace std;class Person { //父类
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }~Person(){cout << "~Person" << endl;}};
class Student : public Person //子类
{public:virtual void BuyTicket() { cout << "买票-半价" << endl; }~Student(){cout << "~Student()" << endl;}};void Func(Person* p)
{p->BuyTicket();
}int main()
{Person Mike;//Func(&Mike);Student Johnson;//Func(&Johnson);return 0;
}

运行结果:
在这里插入图片描述

而在:new 的场景下 要求析构函数必须是 虚函数
请看下面的场景:

Person* ptr = new Person;delete ptr;ptr = new Student;delete ptr;

完整代码如下:


#include<iostream>
using namespace std;class Person { //父类
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }~Person(){cout << "~Person" << endl;}};
class Student : public Person //子类
{public:virtual void BuyTicket() { cout << "买票-半价" << endl; }~Student(){cout << "~Student()" << endl;}};void Func(Person* p)
{p->BuyTicket();
}int main()
{/*Person Mike;Func(&Mike);Student Johnson;Func(&Johnson);*/Person* ptr = new Person;delete ptr;ptr = new Student;delete ptr;return 0;
}

运行上面的代码,我们可以得到下面的结果:
在这里插入图片描述

我们来分析结果出现的原因,

第一个释放:delete ptr 是父类的指针指向的父类的对象 所以调用父类的析构函数,所以在屏幕上打印~Person

第二个释放:delete ptr 是父类的指针指向的子类的对象,但是父类的析构不是虚函数,所以不会去调用子类的析构函数,只会调用父类的析构函数,所以在屏幕上打印:~Person

如何改进:

我们回想一下多态的条件:

那么在继承中要构成多态还有两个条件:

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
    上面已经满足条件1了,因为都是父类的指针

就只差条件2没有满足了,差个虚函数。

虚函数的条件也只差一个 virtual 关键字了,有的兄弟会疑惑不是函数名也不相同吗,那怎么构成虚函数,这里就是构成虚函数的那两个特例中的其中一个,可以回过去看看。

修改的代码:
修改部分,在析构函数上面加上virtual 关键字。

#include<iostream>
using namespace std;class Person { //父类
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }virtual ~Person(){cout << "~Person" << endl;}};
class Student : public Person //子类
{public:virtual void BuyTicket() { cout << "买票-半价" << endl; }virtual ~Student(){cout << "~Student()" << endl;}};void Func(Person* p)
{p->BuyTicket();
}int main()
{/*Person Mike;Func(&Mike);Student Johnson;Func(&Johnson);*/Person* ptr = new Person;delete ptr;ptr = new Student;delete ptr;return 0;
}

在这里插入图片描述

这回就对了。

第一个delete ptr 由于ptr是 Person* 类型的并且指向的是 Person 类型的对象,所以会调用~Person ,在屏幕上打印~Person

第二个 delete ptr, 由于ptr是Person* 类型的 但是指向的却是:Student 类型的变量,满足了多态的条件(1. 父类的指针,2. 虚函数) 所以会先调用子类的析构函数 ~Student ,在屏幕上打印:~Student , 然后再调用父类的析构 在屏幕上打印 ~Person 。 什么? 你问为什么会去调用父类的析构函数,因为子类继承的父类,所以释放子类的空间的时候,要先去调用子类的析构,如果先调用父类的析构会造成问题。

✒下面看一道题来加深我们的理解

HH

答案是 B

![[Pasted image 20240117215245.png]]

首先,我们看一下这个 p->test() , 我们分析一下这个是什么调用。
首先,p 是一个子类的指针,由于子类B是继承了它的父类A的,所以它可以去调用父类的这个函数test() ,但是这里的函数test() 并没有重写,所以不是多态,就是一个普通的调用。

然后,p 会传参,传给test()函数的this指针。

❓ 那这个时候这个this 指针的类型是A* 还是 B*

答案:这里的this 指针还是 A*

首先,B 是 A 的子类, B 是继承的 A ,但是 继承 只是一种比较形象的说法,它并不是直接去在B类中重新开一个test() 函数,而是 让 B 中调用的地方 可以去用A 类中的test()函数,所以它并不会改变test() 函数的参数,所以test()函数中的this 指针的类型还是A*
如果你是A 对象去调用这个test() 函数,那就把A对象传给他,如果是 B 对象去调用,那就是切片 把B 对象中 A 那一部分切出来,然后指向 B 对象中的 A 的那一部分

好 然后我们就来到了这里:
在这里插入图片描述
接下来就是 func() 函数 , 这个就是一个多态调用了,因为她满足了多态调用的两个条件:
在这里插入图片描述

有些兄弟可能会感到疑惑,A 类中的 func() 函数, 和 B 类中的func()函数,他们的参数不同呀(A 类是 int val = 1, B 类是 int val = 0),那怎么构成虚函数的重写?

其实我们认为它们的参数是相同的。 因为我们看的是形参的类型,因为这里形参的类型都是 int 类型的 ,所以我们认为他们是相同的。
举个 例子,如果形参是这样的:A类中(int a = 1), B类中(int b = 2) 只要它的形参都是int 我们认为他们相应的函数都是相同的。

说回来,因为func() 函数构成了多态,所以这里会去调用子类的func() 函数。所以会在屏幕上打印B->

❓ 那到底是打印 B->1 还是 B->0 呢?

有些兄弟会认为会打印B->0 因为 B 类的那个func() 函数的 缺省值给的就是 0 ,这么想就又踩坑了。

首先我们要明白一点,虚函数的重写 继承的是什么?
继承的是父类的接口声明也就是函数的声明。重写的是函数的实现,所以 子类的函数可以不加virtual ,因为只是重写函数的实现,所以函数的参数不会受到影响即:调用时还是调用的父类中虚函数的参数,子类虽然重新写了缺省值但是并没有什么作用,因为它根本不去调用你子类的那个参数,而是直接用父类的,所以这里的val是等于父类中的值的 即: val = 1 ,

所以屏幕上打印的是:B -> 1

📌 函数重载,覆盖(重写) 隐藏(重定义) 的对比

在这里插入图片描述


🏷C++11 override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

📌1. final:修饰虚函数,表示该虚函数不能再被重写

class Car{public:virtual void Drive() final {}};class Benz :public Car{public:virtual void Drive() {cout << "Benz-舒适" << endl;}};

final也可以修饰一个类,这个类不能被继承

📌2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car{public:virtual void Drive(){}};class Benz :public Car {public:virtual void Drive() override {cout << "Benz-舒适" << endl;}};

修饰子类的虚函数 检查它是否完成了重写:如果它没有完成重写就会报错 完成重写了就是正常的


3. 🏷抽象类

📌概念

一个新概念:纯虚函数 , 什么是纯虚函数, 就是在虚函数的后面加上一个 =0
包含纯虚函数的类 就叫做抽象类(也叫接口类) 抽象类的特点就是:不能实例出对象

#include<iostream>
using namespace std;class Car {public:virtual void Drive() = 0; //在虚函数后面加上 =0 就是纯虚函数 }int main() {Car c1; // 这样写就错了 因为抽象类无法实例化对象,就是它定义不出对象Car* c2; //但是它可以定义出指针; return 0;
}

如果父类包含了纯虚函数 则子类也无法实例化出对象:

#include<iostream>
using namespace std;class Car {public:virtual void Drive() = 0; //在虚函数后面加上 =0 就是纯虚函数 };class Benz :public Car
{
public:};int main() {Car c1; // 这样写就错了 因为抽象类无法实例化对象,就是它定义不出对象Car* c2; //但是它可以定义出指针; Benz B1; // 因为父类包含了纯虚函数,所以它这个子类继承了父类 也无法实例化出对象return 0;
}

除非子类对纯虚函数进行了重写

class Benz :public Car
{
public: virtual void Drive() { // 这里重写了这个纯虚函数之后就可以实例化出对象了cout << "Benz-舒适" << endl;}
};

📌接口继承和实现继承

普通函数的继承就是接口继承,虚函数的继承就是实现继承。


4. 🏷多态的原理

// 这里常考一道笔试题:sizeof(Base)是多少?class Base{public:virtual void Func1(){cout << "Func1()" << endl;}private:int _b = 1;};

这道题:答案是 8 (此结果为32位下的)如果是64位 结果是16
为甚是 8 ?
int 类型的 对象 _b 4个字节 + 虚函数表的指针 4 个字节 = 8
通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些

平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代

表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数

的地址要被放到虚函数表中,虚函数表也简称虚表。

不管你继承没有继承 只要你有虚函数 就会有虚函数表。

虚函数的地址存放在虚函数表中的。

📌多态调用和普通调用是什么时候确定地址的?

在这里插入图片描述

📌 多态的其中一个条件是指针和引用,那为什么对象不行?

指针和引用都是指向了子类对象中切片出来的属于父类的那一部分, 但是对象的话是将子类对象中父类的那一部分成员拷贝给父类,但并不会拷贝虚函数表指针,所以使用对象不行。

📌 如果虚函数不重写,父类和子类的虚函数表指针一样不一样?

它们虚标里面的内容是一样的但是地址不一样
在这里插入图片描述

📌 同类对象的虚表一样不一样?

在这里插入图片描述

这个时候是一样的了:
在这里插入图片描述

本章图集

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

将Flutter程序打包为ios应用并进行安装使用

如果直接执行flutter build ios: Building com.example.myTimeApp for device (ios-release)...════════════════════════════════════════════════════════════════════════════════No vali…

Web自动化 - selenium

文章目录 一、selenium的使用selenium的安装 二、元素1. 定位选择元素1.id 定位2. class_name 定位find_element 和 find_elements的区别3. TAG_NAME 定位4. 超链接 定位 2. 操控元素1. 查询内容2. 获取元素文本内容3. 获取元素属性 3. 浏览器常用操作API4. 鼠标操作 - perform…

Python 全栈系列244 nginx upstream 负载均衡 踩坑日记

说明 最初是因为租用算力机(Python 全栈系列242 踩坑记录:租用算力机完成任务)&#xff0c;所以想着做一个负载均衡&#xff0c;然后多开一些服务&#xff0c;把配置写在nginx里面就好了。 一开始租用了一个3080起了一个服务&#xff0c;后来觉得速度不够快&#xff0c;再起了…

DOM 文档对象模型

一、DOM简介 1、什么是DOM DOM 文档对象模型简称&#xff0c;是W3C组织推荐的处理可扩展标记语言的标准编程接口 W3C已经定义了一系列的DOM接口&#xff0c;通过这些接口可以改变网页的内容、结构、样式 2、DOM树 DOM把以上内容都看做是对象 二、获取元素 获取页面元素&am…

day001 ~如何修改主机名

命令行方式设置主机名 # 这个很重要&#xff01;用命令改方便些 hostnamectl set-hostname ocloud-252 #查询&#xff0c;exit或logout重新登录后发现主机名换掉 hostname nmtui方式修改 nmtui 在工作中,如果机器很多,最好修改主机名做好标识不至于弄混,方便管理.

LeetCode—用队列实现栈

一.题目 二.思路 1.后入先出的实现&#xff1a; 创建两个队列来实现栈&#xff08;后入先出&#xff09;&#xff1a; 两个队列&#xff0c;保持一个存数据&#xff0c;另一个为空&#xff0c;入数据&#xff08;push&#xff09;要入不为空的队列&#xff0c;&#xff08;p…

DDS块集是如何工作的?

DDS块集使你能够在Simulink中创建DDS应用程序。如果你有一个在Simulink中建模的应用程序&#xff0c;希望能够使用DDS&#xff0c;则可以使用DDS块集轻松连接到DDS中间件平台。 DDS块集将DDS概念引入Simulink环境&#xff0c;在Simulink应用程序中对这些概念进行建模&#xff0…

STM32串口通信入门

文章目录 一、串口协议和RS-232标准&#xff0c;以及RS232电平与TTL电平的区别1.串口通信协议2.RS-232标准3.RS232电平与TTL电平的区别4.USB/TTL转232“模块&#xff08;CH340芯片为例&#xff09; 二、补充实验&#xff08;一&#xff09;几个常见的库函数、结构体1.时钟配置函…

【机器学习数据可视化-04】Pyecharts数据可视化宝典

一、引言 在大数据和信息爆炸的时代&#xff0c;数据可视化成为了信息传递和展示的关键手段。通过直观的图表和图形&#xff0c;我们能够更好地理解数据&#xff0c;挖掘其背后的信息。Pyecharts&#xff0c;作为一款基于Python的数据可视化库&#xff0c;凭借其丰富的图表类型…

多模态EDA论文小记

论文地址 该论文主要改进点是&#xff1a;通过动态化局部搜索中每个集群大小&#xff0c;高斯和柯西分布共同产生个体。总的来说改进点不多&#xff0c;当然也可能是笔者还没发现。 局部搜索 划分集群 划分集群有两个策略分别是&#xff1a; 随机生成一个点作为中心点&…

MySQL表死锁查询语句

步骤1&#xff1a;查询表死锁的sql语句&#xff1a; SELECT * FROM information_schema.PROCESSLIST where length(info) >0 ; 或 SELECT * FROM information_schema.INNODB_TRX; 步骤2&#xff1a;删除 kill "对应的线程id"

【JVM】Class文件的格式

目录 概述 Class文件的格式 概述 Class文件是JVM的输入&#xff0c;Java虚拟机规范中定义了Class文件的结构。Class文件是JVM实现平台无关、技术无关的基础。 1:Class文件是一组以8字节为单位的字节流&#xff0c;各个数据项目按顺序紧凑排列 2:对于占用空间大于8字节的数据…

16 华三数据中心最流行的技术 M-LAG

STP和MTP&#xff08;第二十二课&#xff09;-CSDN博客 VRRP技术和浮动路由(第二十六课)_vrrp 浮动路由-CSDN博客 VRRP DHCP ACL NAT 网络核心路由技术综述 (第十课)-CSDN博客 04 交换机的IRF的配置-CSDN博客 1 M-LAG AI介绍 M-LAG&#xff08;Multi-Chassis Link Aggrega…

其他的 框架安全:Apache Solr 远程代码漏洞.(CVE-2019-0193)

什么是 Apache Solr Apache Solr是一个开源的搜索服务&#xff0c;便用Java语言开发&#xff0c;主要基于 HTTP 和ApacheLucene 实现的。Sor是一个高性能&#xff0c;采用Java5开发&#xff0c;基于Lucene的全文搜索服务器。 目录&#xff1a; 什么是 Apache Solr 生成的漏…

基于yolov5+streamlit目标检测演示系统设计

YOLOv5与Streamlit&#xff1a;智能目标检测可视化展示介绍 随着人工智能技术的飞速发展&#xff0c;目标检测技术已成为推动智能化社会进步的关键技术之一。在众多目标检测算法中&#xff0c;YOLOv5以其卓越的性能和实时性&#xff0c;成为了业界的佼佼者。与此同时&#xff…

Spring Boot 集成 sa-token 实践教程

Spring Boot 集成 sa-token 实践教程 sa-token 是一个轻量级且功能强大的权限认证框架&#xff0c;它基于Java语言&#xff0c;专为Java开发者设计&#xff0c;以简化权限管理的复杂性。在Spring Boot项目中集成sa-token&#xff0c;可以快速实现会话管理、权限控制等功能。本文…

程序员必读书单(CSDN专享)

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的金融摸鱼侠&#xff0c;欢迎大家加入 Java 人自己的交流群“共同富裕的 Java 人”。 今天是一篇分享资源的汇总&#xff0c;近半年来我总计分享了 202 本&#xff08;将近 10G &#xff09;的计算机领域著作…

智慧法治:AI技术如何赋能法律行业创新

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

Git 的原理与使用(上)

Git是一个分布式版本控制系统&#xff0c;它被广泛用于协作开发和管理软件项目。开发人员可以通过Git来跟踪文件的变化、协调工作、并管理项目的不同版本。 Git允许用户在不同的分支上开发新功能&#xff0c;然后合并这些分支并确保团队成员之间的工作协调一致。此外&#xff…

微信小程序踩坑,skyline模式下,scroll-view下面的一级元素设置margin中的auto无效,具体数据有效

开发工具版本 基础库 开启skyline渲染调试 问题描述 skyline模式下,scroll-view下面的一级元素的margin写auto的值是没有效果的(二级元素margin写auto是有效果的),关闭这个模式就正常显示 演示效果图 父元素的宽度和高度效果(宽度是750rpx,宽度占满的) 一级元素宽度和css效果…