【C++】---继承

【C++】---继承

  • 一、继承的概念及定义
    • 1、继承的概念
    • 2、定义语法格式
    • 3、继承基类成员访问方式的变化
  • 二、基类 和 派生类 的对象之间的赋值转换
    • 1、赋值规则
    • 2、切片
      • (1)子类对象 赋值 给 父类对象
      • (2)子类对象 赋值 给 父类指针
      • (3)子类对象 赋值 给 父类引用
  • 三、继承中的作用域
    • 1、父类和子类都有独立的作用域
    • 2、隐藏的概念
    • 3、如果是成员函数的隐藏,只需要函数名相同 就构成 隐藏
    • 4、继承中最好不要定义同名成员
  • 四、派生类的默认成员函数
    • 1、规则①
    • 2、规则②
    • 3、规则③
    • 4、规则④
  • 五、继承和友元
  • 六、继承和静态成员
  • 七、菱形继承
    • 1、单继承
    • 2、多继承
    • 3、菱形继承
  • 八、菱形虚拟继承
    • 1、虚继承
    • 2、虚继承的解决数据冗余 和 二义性的原理
  • 九、继承和组合

一、继承的概念及定义

1、继承的概念

继承的概念:

继承机制是面向对象程序设计当中使代码复用的重要手段,它允许程序员,在保持原有类特性的基础上,进行拓展,增加功能,产生新的类,这个新的类就叫做:派生类。

继承的出现,呈现出面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

以前我们所接触到的复用都是函数的复用,而继承的出现则是体现了:类设计层次的复用。
在这里插入图片描述

2、定义语法格式

在这里插入图片描述

#include<iostream>
using namespace std;// 基类
class Person
{
public:void Print(){cout << "name->" << _name << endl;cout << "age->" << _age << endl;}
protected:string _name = "peter";int _age = 18;
};// 派生类:stu
class Student :public Person
{
protected:int stu_ID;
};
// 派生类:tea
class Teacher :public Person
{
protected:int job_ID;
};int main()
{Student s;Teacher t;s.Print();t.Print();return  0;
}

3、继承基类成员访问方式的变化

在这里插入图片描述
不需要记住总结上面的表
我给大家总结两点,只需要记住下面两点规律,一切都好说:

(1)基类private成员在派生类中无论以什么方式继承都是不可见的

(2)基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),访问权限,由大到小:public>protected>private。(Min(成员在基类的访问限定符,继承方式):意思是 取 两者访问权限较小的!)


注意:基类private成员在派生类中无论以什么方式继承都是不可见的

关于什么叫做不可见:

假如将父类成员改为private,那么子类继承了父类的私有成员,内存上,子类对象有这个成员,但是语法上不允许访问
可以理解为父类就是你的父亲,而父类的私有成员就是你父亲藏的私房钱。子类就是你自己,不可见的意思,就是说:你知道你父亲藏有私房钱,但是你找不到你见不到!

二、基类 和 派生类 的对象之间的赋值转换

1、赋值规则

(1)派生类对象可以赋值给:基类对象 / 基类的指针 / 基类的引用。这里有一个形象的说法叫做切割或者切片,就是将派生类中父类那一部分切割下来,赋值过去。(子可以给父)

(2)基类对象不能赋值给派生类对象。(父不能给子)

(3)基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTimeType Information)的dynamic_cast 来进行识别后进行安全转换。(ps:这个我们后面再讲解,这里先了解一下)

2、切片

(1)子类对象 赋值 给 父类对象

子可以赋值给父,但 父不可以赋值给子:
在这里插入图片描述

// 基类
class Person
{
public:void Print(){cout << "name->" << _name << endl;cout << "age->" << _age << endl;}string _name = "peter";int _age = 18;
};// 派生类:stu
class Student :public Person
{
public:int stu_ID;
};int main()
{Student s;// s是一个 子类对象Person p;// p是一个 父类对象s._name = "yjl";s._age = 20;s.stu_ID = 20221071;p = s;s = p;return  0;
}

在这里插入图片描述

(2)子类对象 赋值 给 父类指针

在这里插入图片描述

① 子类对象可以赋值给父类指针


// 基类
class Person
{
public:void Print(){cout << "name->" << _name << endl;cout << "age->" << _age << endl;}string _name = "peter";int _age = 18;
};// 派生类:stu
class Student :public Person
{
public:int stu_ID;
};int main()
{Student s;// s是一个 子类对象//Person p;// p是一个 父类对象Person* p;// p是一个 父类对象指针s._name = "yjl";s._age = 20;s.stu_ID = 20221071;p = &s;return  0;
}

在这里插入图片描述
② 若父类指针要赋值给子类指针,必须将父类指针强转为子类指针:

int main()
{//父类指针赋值给子类指针Person p;//父类对象Student *s;//子类指针p._name = "yjl";p._sex = "male";p._age = 6;Person* pp = &p;//父类指针s = (Student*)pp;//将父类指针强转为子类型指针后,再赋值给子类型指针return 0;
}

③ 指向父类对象的父类指针,强转为子类指针后赋值给子类指针可能会造成访问越界

int main()
{//指向父类对象的父类指针,强转为子类指针后赋值给子类指针可能会造成访问越界Person p;p._name = "yjl";p._sex = "male";p._age = 7;//s._No = 21530;Person* pp = &p;Student* ss = (Student*)pp;//ss->_No = 6;访问越界造成程序崩掉return 0;
}

(3)子类对象 赋值 给 父类引用

// 基类
class Person
{
public:void Print(){cout << "name->" << _name << endl;cout << "age->" << _age << endl;}string _name = "peter";int _age = 18;
};// 派生类:stu
class Student :public Person
{
public:int stu_ID;
};int main()
{Student s;// s是一个 子类对象//Person p;// p是一个 父类对象//Person* p;// p是一个 父类对象指针s._name = "yjl";s._age = 20;s.stu_ID = 20221071;Person& p=s;// p是一个 父类对象引用return  0;
}

在这里插入图片描述

三、继承中的作用域

1、父类和子类都有独立的作用域

2、隐藏的概念

(1)子类和父类中有同名成员,子类成员将屏蔽父类,对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

(2)通过代码来演示:隐藏!

class Person
{
public:string _name = "yjl";int _age = 20;void Print1(){cout << "name->" << _name << endl;cout << "age->" << _age << endl;}
};class Student :public Person
{
public:int _age = 100;void Print2(){cout << "age->" << _age << endl;}
};int main()
{Person p;Student s;s.Print2();return 0;
}

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

3、如果是成员函数的隐藏,只需要函数名相同 就构成 隐藏

// 父类
class Person
{
public:string _name = "yjl";int _age1 = 20;void Print(){cout << "name->" << _name << endl;cout << "age->" << _age1 << endl;}
};
// 子类
class Student :public Person
{
public:int _age2 = 100;void Print(int count){while (count){cout << "age->" << _age2 << endl;count--;}}
};
int main()
{Person p;Student s;// 这里的s.Print();与父类的Print  构成函数重载×××,构成函数隐藏√√√// 父子类函数构成隐藏,只需要满足一点即可(函数名相同)s.Print();// 这里认为你用子类对象调用的是子类的打印函数,而不是你想要调用的父类打印函数。return 0;
}

在这里插入图片描述

4、继承中最好不要定义同名成员

四、派生类的默认成员函数

(1)什么叫做默认成员函数?

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

(2)派生类的默认成员函数和普通类的默认成员函数的不同区别:

  1. 派生类的构造函数必须要调用基类的构造函数来初始化基类的那一部分成员。如果基类没有写默认的构造函数,那么就必须要在派生类的初始化列表里面对基类的构造函数进行显示调用。(构造:先父后子)
  2. 派生类的拷贝构造函数必须要调用基类的拷贝构造函数,来进行对基类成员的拷贝构造。
  3. 派生类的operator=必须要调用基类的operator=完成基类的赋值
  4. 派生类的析构函数,会在被调用完成之后,自动调用基类的析构函数来清理基类的成员。因为这样才能保证析构函数作用的时候是先析构子类的成员,最后再析构父类的成员的顺序(析构:先子后父)
  5. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加显示调用的情况下,子类析构函数和父类析构函数构成隐藏关系。

总之:派生类=基类+派生类成员(①把基类看作是一个自定义类型的整体->无论是构造机构,拷贝构造,基类就调用自己的默认写的!②派生类成员看作一个普通的类!)

// 父类
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(){cout << "~Person()" << endl;}// 赋值运算符重载Person& operator=(const Person& p){cout << "Person& operator=()" << endl;if (this != &p){_name = p._name;}return *this;}
protected:string _name;
};// 子类
class Student :public Person
{
public:// 构造Student(const char* name,int num):Person(),_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){cout << "Student& operator=" << endl;if (this != &s){Person::operator=(s);_num = s._num;}return *this;}// 析构~Student(){cout << "~Student()" << endl;}
protected:int _num;// 学号
};void test()
{Person p1;Student s1("yjl", 2022);//Student s2("yjl666", 2024);//Student s3(s1);//Person p2(s3);}int main()
{test();return 0;
}

1、规则①

规则①:子类的构造函数必须调用父类的构造函数初始化父类的那一部分成员。如果父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。

(1)回忆一下什么是默认构造函数?

  1. 我们没有写编译器,自动生成的。
  2. 我们自己写了无参的构造函数。
  3. 我们自己写了的,带参的全缺省构造函数。

(2)父类里面没有写默认的构造函数和拷贝构造函数:
在这里插入图片描述
当父类没有写默认的构造函数的时候,显式调用父类默认构造函数,是把当作一个整体去调父类的构造函数!
(3)父类里面写了默认的构造函数:

在这里插入图片描述
总结:
(1)父类有默认构造函数,子类只需要在构造函数中初始化子类自己的成员
(2)父类没有默认构造函数,子类要在构造函数初始化列表显式调用父类的构造函数,再初始化子类自己的成员

2、规则②

规则②:子类的拷贝构造函数必须调用父类的拷贝构造完成父类的拷贝初始化。

子类的拷贝构造函数该如何写呢?

调用父类拷贝构造函数时,将子类对象作为参数传给父类拷贝构造函数,这时会发生切片行为,将子类对象中父类成员部分切片赋值给父类成员,完成父类拷贝构造;再将子类自己的成员初始化为拷贝对象的子类成员值:

3、规则③

规则③ 子类的operator=必须要调用父类的operator=完成父类的赋值

如果子类的赋值运算符重载函数里只调用子类的operator=,那么会发生栈溢出:

// 赋值运算符重载Student& operator=(const Student& s){cout << "Student& operator=" << endl;if (this != &s){operator=(s);_num = s._num;}return *this;}void test()
{//Person p1;Student s1("yjl", 2022);Student s2("yjl666", 2024);s1 = s2;// 赋值的时候,会发生:栈溢出//Person p2(s3);}

在这里插入图片描述
这样写才对:

	// 赋值运算符重载Student& operator=(const Student& s){cout << "Student& operator=" << endl;if (this != &s){Person::operator=(s);_num = s._num;}return *this;}

4、规则④

规则④ 子类的析构函数会在被调用完成后自动调用父类的析构函数清理父类成员。因为这样才能保证子类对象先清理子类成员再清理父类成员的顺序
1、
(1)析构顺序:先子后父!

(2)构造顺序:先父后子!

在这里插入图片描述
2、注意:由于多态的缘故,对析构的统一处理

// 对析构的统一处理:~Student(){//Person::~Person();~Person();cout << "~Student()" << endl;}

在这里插入图片描述
这是由于父类的析构函数和子类的析构函数构成隐藏,奇不奇怪?

按照隐藏的定义,父类和子类的同名函数才会构成隐藏,为什么父类和子类的析构函数不同名也会构成隐藏呢?

这是因为多态的缘故,任何类的析构函数名都会被统一处理成destructor( )。所以父类和子类析构函数同名,会构成隐藏。解决方法就是加父类作用域,指定访问的是父类的析构函数就可以了:

	// 析构~Student(){Person::~Person();//~Person();cout << "~Student()" << endl;}

在这里插入图片描述

五、继承和友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
void main()
{Person p;Student s;Display(p, s);
}

六、继承和静态成员

如果在父类里面定义了一个static静态成员,那么在整个继承体系当中,无论父类派生出多少个子类,整个体系只有这一个静态成员!

下面这些代码是对静态成员变量的诠释,在整个继承体系中,只要有static静态成员变量,那么它就是同一个!

#include<iostream>using namespace std;class Person
{
public:Person() { _count++; }
protected:string _name;
public:static int _count;
};int Person::_count = 0;class Student : public Person
{
protected:int _ID;
};class Graduate : public Student
{
protected:string _course;
};void TestPerson()
{Student s1;Student s2;Student s3;Graduate s4;// 在上面的继承体系中,它是单继承。// 创建子类对象的时候必须要先构造父类对象,所以进行到此处_count等于4。cout << "人数:" << Person::_count << endl;Student::_count = 0;// 因为已经说明了_count是一个静态的,在整个继承体系中它就是一个整体就是一个// 对_count修改后他就是0了。cout << "人数:" << Person::_count << endl;
}int main()
{TestPerson();return 0;
}

在这里插入图片描述

七、菱形继承

1、单继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

在这里插入图片描述

2、多继承

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

在这里插入图片描述

3、菱形继承

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

在这里插入图片描述

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 _course;
};void Test()
{Assistant a;a._name = "peter";// 这样会有二义性无法明确知道访问的是哪一个_name// 因为,a访问的_name没有指明是student继承person的_name,还是teacher继承person的_name。// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "xxx";
}

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

在这里插入图片描述

八、菱形虚拟继承

虚拟继承:可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。
需要注意的是,虚拟继承不要在其他地方去使用。

1、虚继承

虚继承的语法格式:在两个直接父类的继承方式访问限定符前加vitual:

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 _course;
};void Test()
{Assistant a;a._name = "peter";/*a.Student::_name = "xxx";a.Teacher::_name = "xxx";*/
}int main()
{Test();return 0;
}

在这里插入图片描述

2、虚继承的解决数据冗余 和 二义性的原理

虚拟继承解决数据冗余和二义性的原理:

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型

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;
}

下图是菱形继承的内存对象成员模型:这里可以看到数据冗余:
在这里插入图片描述

下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。

在这里插入图片描述
下面是上面的Person关系菱形虚拟继承的原理解释:

在这里插入图片描述

九、继承和组合

  1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的OO语言都没有多继承,如Java。
  3. 继承和组合:

(1)public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。

(2)组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

(3)优先使用对象组合,而不是类继承 :链接:优先使用对象组合,而不是类继承

(4)实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。

// Car和BMW Car和Benz构成is-a的关系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;}};// Tire和Car构成has-a的关系class Tire{protected:string _brand = "Michelin";  // 品牌size_t _size = 17;         // 尺寸};class Car{protected:string _colour = "白色"; // 颜色string _num = "陕ABIT00"; // 车牌号Tire _t; // 轮胎};

好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!

在这里插入图片描述

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

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

相关文章

数据结构-线性表-链表-2.3-1

设计一个递归算法&#xff0c;删除不带头结点的单链表L中所有值为x的结点。 void del(Linkllist &L&#xff0c;int x){LNode *p;if(LNULL){return;}if(L->datax){pL;LL->next;;free(p);del(L,x);}else{del(L->next,x);} } 时间复杂度为O(n)

【项目学习01_2024.05.08_Day06】

学习笔记 5 新增课程5.1 需求分析5.1.1 业务流程5.1.2 数据模型 5.2 接口定义5.3 接口开发5.3.1 保存课程基本信息5.3.2 保存营销信息 5.4 接口测试 5 新增课程 5.1 需求分析 5.1.1 业务流程 5.1.2 数据模型 5.2 接口定义 5.3 接口开发 根据需求分析&#xff0c;新增课程表…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

NSSCTF中的web

目录 [第五空间 2021]WebFTP [LitCTF 2023]PHP是世界上最好的语言&#xff01;&#xff01; [SWPUCTF 2021 新生赛]PseudoProtocols [LitCTF 2023]导弹迷踪 [NISACTF 2022]easyssrf [第五空间 2021]WebFTP 1.进入页面&#xff0c;发现是登录页面&#xff0c;想到 弱口令&…

《MySQL45讲》读书笔记

重建表 alter table t engine InnoDB&#xff08;也就是recreate&#xff09;&#xff0c;而optimize table t 等于recreateanalyze&#xff0c;让表大小变小 重建表的执行流程 建立一个临时文件&#xff0c;扫描表 t 主键的所有数据页&#xff1b;用数据页中表 t 的记录生…

六步轻松打造创新文化和领导力的技术团队

引言 在当今快速变化的商业环境中&#xff0c;构建一个支持创新的文化和领导力至关重要。通过实施创新激励机制、建立失败的容忍度、领导层的示范作用、开放和透明的沟通、促进多样性和包容性以及持续学习和适应&#xff0c;公司可以培养出一支能够不断创新的员工队伍&#xf…

6层板学习笔记2

说明:笔记基于6层全志H3消费电子0.65MM间距BGA 67、多层板的电源建议直接大面积铺铜,不建议走线,铺铜充分满足其载流能力 68、凡亿推荐表层1OZ的铜厚线宽20MIL能承载1A的电流,内层0.5OZ的铜厚线宽为40MIL能承载1A的电流,过孔直径20MIL(0.5MM)能承载1A左右的电流,实际设…

QT实战百度语音识别

前言 随着学习的深入&#xff0c;感觉愈发缺乏满足感。刚好看到微信语音转文字的功能&#xff0c;经网上查询&#xff0c;发现可以使用 QT 百度语音识别技术 实现这一功能。当然&#xff0c;由于使用的 QT 和 百度语音识别&#xff0c;那么看不到一些具体的底层实现&#xff…

C++初识多态(1)

1.多态要解决的问题&#xff08;引入&#xff09; 任何一种机制的存在&#xff0c;必然是有其存在的意义的&#xff0c;例如我们前面学过的函数重载&#xff0c;运算符重载&#xff0c;以及引用等等&#xff0c;都是解决一些特殊问题的&#xff1b; 下面通过一些具体的例子&a…

Echarts散点图的29个配置项,散点图可以随心所欲啦。

1-9 当使用 ECharts 绘制散点图时&#xff0c;可以配置以下一些常用的选项&#xff1a; 1. tooltip&#xff1a;配置提示框组件&#xff0c;用于显示鼠标悬停在散点上时的提示信息。 2. legend&#xff1a;配置图例组件&#xff0c;用于展示不同散点的标识和名称。 3. xAxis…

目录页码右对齐快速解决

选择目录–段落–制表符&#xff0c;按图中设置即可

merge函数占用内存过大

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

USB3.0接口——(1)基础知识

1.背景 USB 3.0是一种USB规范&#xff0c;该规范由英特尔等公司发起。 USB协议版本 命名约定 USB-IF组织引入命名约定&#xff0c;将端口列为 USB 5 Gbps、USB 10 Gbps、USB 20 Gbps 、USB 40 Gbps&#xff0c;而不使用版本号。获得 USB-IF 认证的 USB 产品的制造商会获得带…

WM Shell多动画场景处理

Shell导致的内存泄漏 基本上都是某个动画未正常结束&#xff0c;执行时间太久导致后续动画堆积或被merge到异常动画导致相关Surface得不到释放导致的。 某个Transition执行时间太久导致后续动画堆积 Visible layers 中有1558 个Transition Root相关layer Visible layers (c…

Java毕业设计 基于SpringBoot vue社区智慧养老监护管理平台

Java毕业设计 基于SpringBoot vue社区智慧养老监护管理平台 SpringBoot 社区智慧养老监护管理平台 功能介绍 登录注册 个人中心 修改密码 个人信息 房间信息管理 房间入住信息管理 反馈信息管理 留言管理 老人信息管理 公告管理 物资申请管理 管理员管理 护工管理 体检员管理…

C++多态(全)

多态 概念 调用函数的多种形态&#xff0c; 多态构成条件 1&#xff09;父子类完成虚函数的重写&#xff08;三同&#xff1a;函数名&#xff0c;参数&#xff0c;返回值相同&#xff09; 2&#xff09;父类的指针或者引用调用虚函数 虚函数 被virtual修饰的类成员函数 …

React:Router-1.BrowserRouter组件式

使用步骤 安装 react-router-dom 依赖 $ npm install react-router-dom6导入 BrowserRouter, Link, Routes, Route 对象 import {BrowserRouter, Link, Routes, Route} from react-router-dom;3.BrowserRouter&#xff1a;history模式路由&#xff1b; HashRouter&#xff1…

代码随想录算法训练营第二十二天|654.最大二叉树 、617.合并二叉树 、700.二叉搜索树中的搜索 、 98.验证二叉搜索树

654.最大二叉树 文档讲解&#xff1a;代码随想录 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 递归的三要素&#xff1a; 第一要素&#xff1a;明确这个函数想要干什么 传入一个数组&#xff0c;针对这个数组构造一个最大二叉树 第二要素&#xff1a;寻…

跟TED演讲学英文:What moral decisions should driverless cars make by Iyad Rahwan

What moral decisions should driverless cars make? Link: https://www.ted.com/talks/iyad_rahwan_what_moral_decisions_should_driverless_cars_make Speaker: Iyad Rahwan Date: September 2016 文章目录 What moral decisions should driverless cars make?Introduct…

kubeflow简单记录

kubeflow 13.7k star 1、Training Operator 包括PytorchJob和XGboostJob&#xff0c;支持部署pytorch的分布式训练 2、KFServing快捷的部署推理服务 3、Jupyter Notebook 基于Web的交互式工具 4、Katib做超参数优化 5、Pipeline 基于Argo Workflow提供机器学习流程的创建、编排…