C++进阶-->继承(inheritance)
1. 继承的概念及定义
1.1 继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要手段,他允许我们在保证原有类的特性基础上还进行扩展,通过继承产生的类叫做派生类(子类),被继承的类叫做基类(父类)。继承呈现了面向对象程序设计的层次结构。和我们之前Date类那里重载运算符的那块有异曲同工之妙,重载运算符那里的实现是函数的复用,而继承则是类设计层次的复用。
ok, 当我们想创建两个类一个是学生,一个是教师,他们都有一些共同的特点例如名字、地址、电话号码、年龄等等的时候,但他们也有不同的一些特点,例如学生有学号,老师有职称等等,我们就可以用继承来实现,创建一个父类Person储存他们的共同特点,然后再对父类进行继承出新的子类即可;
语法为:
class Person{};
class Teacher: public: Person{};
class Student: public: Person{};
当我们想实现一个Student类和Teacher类的时候会有一些相同的信息;如下代码所示:
#include<iostream>
using namespace std;class Student
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证void identity(){// ...}// 学习void study(){// ...}
protected:string _name = "peter"; // 姓名string _address; // 地址string _tel; // 电话int _age = 18; // 年龄int _stuid; // 学号
};
class Teacher
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证void identity(){// ...}// 授课void teaching(){//...}
protected:string _name = "张三"; // 姓名int _age = 18; // 年龄string _address; // 地址string _tel; // 电话string _title; // 职称
};
int main()
{return 0;
}
那么我们就可以用继承进行解决代码如下:
#include<iostream>
using namespace std;class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "张三"; // 姓名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都有Person类的成员例如name、address、tel,而有不需要我们再过多写;
首先又在这里提一嘴就是成员变量被protected修饰的话就代表他的子类或者内部类可以使用,而在类外或者派生类(子类)外都不可以使用,private则是子类和类外都不能使用;
1.2 继承的定义
定义的格式:
如下图可见:
1. 如果基类(父类)的成员是private的话,在派生类(子类)不可以被访问,但是这些成员也一样会被继承到派生类去,只是不能访问而已。
2. 基类(父类)的成员是protected的话,在派生类(子类)中可以被访问。而protected关键词其实就是因为继承才出现的。
3. 继承方式,一般是按最小级别的来,级别如:public > protected > private ,如果说继承方式是public的话,基类成员是private修饰的,那么基类的成员访问限定符按最小级别来即private;如果说继承方式是private的话,无论基类成员是public还是protected,基类的访问限定符都为private。
4. 使用关键字class时默认的继承方式是private,而struct时默认方式是public;但最好显示写出来。
5. 一般都是用public,便于维护;
1.3 继承类模板
顾名思义继承一个类模板;如下代码和注释来看:
#include<iostream>
#include<vector>
#include<list>
#include<deque>
#define Container std::vector
using namespace std;namespace lwt
{//template<class T>//class vector//{};// stack和vector的关系,既符合is-a,也符合has-a//is-a是说stack底层实现就是vector//has-a是说包含关系,即vector可以被stack继承,因为stack的特性包含vector的特性template<class T>class stack : public std::vector<T>{public://👇如果说我们直接调用的push_back(x);的话会出错,因为按需实例化的时候//push_back在本类里面找不到然后就会进到父类里面找,但都没找到,所以我们要指定一下//类域即vector<T>::void push(const T& x){// 基类是类模板时,需要指定⼀下类域,// 否则编译报错:error C3861: “push_back”: 找不到标识符// 因为stack<int>实例化时,也实例化vector<int>了// 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到//push_back(x);vector<T>::push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};//下面是上面的一个新用法//我们可以使用宏在头部定义一个Container放置std::vectortemplate<class T>class stack : public Container<T>{public:void push(const T& x){vector<T>::push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};
这里再来讲一下里面为什么要用vector<T>::push_back,因为模板是遵守按需实例化的规则来的,class stack : public std::vector<T>,如果我们我们实例化stack<int>,那就只实例化了vector<int>,编译器不知道我们还要实例化里面的功能,这里只是单纯实例化了一个vector<int>对象,所以我们需要指定一下vector<T>域内的push_back即可;
2. 基类和派生类之间的转换
- public继承的派生类(子类)对象可以给基类(父类)的对象、指针和引用赋值。但不是把派生类(子类)对象的所有成员都赋值给基类(父类)对象,而是把原先属于基类部分的成员赋值给基类对象;这个过程称为切割或者切片;很好理解,就是把派生类内不是从基类继承来的成员切割掉;
- 基类的对象不能赋值给派生类的对象;
- 但是如果那个基类的对象的指针原先就是指向派生类的指针的话,那还是可以通过强制类型转换赋值给派生类对象的,但是这里使用到一个dynamic_cast,这里只是演示一下;如下代码所示:
#include<iostream>using namespace std; class Person { protected://多态后面会说virtual void func(){} public:Person(const string& name, const string& sex, int age):_name(name),_sex(sex),_age(age){cout << "Person(const string& name, const string& sex, int age)" << endl;cout << "name:> " << _name << endl;cout << "sex:> " << _sex << endl;cout << "age:> " << _age << endl;}string _name; // 姓名string _sex; // 性别int _age; // 年龄 };class Student :public Person { public:Student(const string& name="lwt", const string& sex="man", int age=20):Person(name, sex, age){}int _No; //学号 };int main() {Student sobj;//子类对象可以赋值给父类对象/指针/引用//这样就是上面所说的切割,即会把非父类对象的部分切割然后//把是父类对象的部分赋值给父类Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;//基类的指针或者引⽤可以通过强制类型转换赋值给派⽣类的指针或者引⽤。但是必须是基类的指针//是指向派⽣类对象时才是安全的。这⾥基类如果是多态类型,可以使⽤RTTI(Run - Time Type//Information)的dynamic_cast 来进⾏识别后进⾏安全转换。(ps:这个我们后⾯类型转换章节再//单独专⻔讲解,这⾥先提⼀下)Student* sp1 = dynamic_cast<Student*>(pp);cout << sp1<< endl;cout << pp << endl;pp = &pobj;Student* sobj2 = dynamic_cast<Student*>(pp);//2.父类对象不能赋值给子类对象,这里会编译报错//sobj = (Student)pobj;return 0; }
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
这里需要提一点的就是,在派生类想要初始化基类的话需要调用基类的构造函数,不可以直接使用基类的成员变量在派生类的初始化链表初始化;
3. 继承的作用域
3.1 隐藏
1. 在继承体系中,基类和派生类都有独立的作用域;
2. 派生类和基类中如果有同名的成员的话,那么就会屏蔽掉基类的成员访问,这种就称作隐藏;那如果我们在派生类想要访问基类中被隐藏的成员我们就可以通过指定访问域进行访问例如:基类::基类成员即可;
3. 如果是成员函数的话,构成隐藏的条件只需要一个即函数名相同,即使他们的参数不相同也不会构成重载,照样构成隐藏;如下代码所示:
#include<iostream>
using namespace std;class Person
{
public:void Fun(){cout << "PersonFunc()" << endl;}void test(int a){cout << "Persontest()" << endl;}
protected:string _name = "小李子"; // 姓名int _num = 111; // 身份证号
};//隐藏即如果基类和派生类有同名的成员的话
//基类的成员就会被隐藏,只是用派生类的成员
//如果我们想使用基类里面的话我们就需要指定类域
//即可;//如果说有函数名相同的则会直接隐藏基类的函数,不管他们参数是否一样
//不会构成重载
//只会被隐藏掉class Student : public Person
{
public:void Print(){cout << _num << endl;cout << Person::_num << endl;}void Func(){cout << "StudentFunc()" << endl;}void test(){cout << "Studenttest()" << endl;}protected:int _num = 999; // 学号
};int main()
{Student s1;s1.Print();s1.Func();s1.Person::Fun();//报错//s1.test(1);s1.Person::test(1);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
看完这个知识点我们直接就来两道面试题;如下图
#include<iostream>
using namespace std;
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};
int main()
{B b;b.fun(10);b.fun();return 0;
};
答案:A,A;
解析:首先上面说了,只要函数名相同就构成隐藏,不管他参数相同还是不同,都不会构成重载;所以第一题选A。
第二题:由于是构成隐藏关系,那我们想访问那个无参的func的话就必须显式调用即b.A::func();指定一定访问域,不然就报错;
4. 派生类的默认成员函数
默认的意思就是指我们不写,编译器会帮我们自动生成一个;
1. 基类如果没有默认构造的话,那么在派生类的构造函数里需要调用基类的构造函数对基类的成员进行初始化,调用基类的构造函数就类似于匿名对象一样,但这里不叫匿名对象;如下代码所示:
#include<iostream>
using namespace std;class Person
{
public:Person(const string& name ):_name(name){cout << "Person()" << endl;}protected:string _name;
};class Student:public Person
{
public:Student(const string& name, int num):Person(name),_num(num){cout << "Student()" << endl;}protected:int _num;
};int main()
{Student s1("lwt", 12);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
2. 拷贝构造的也是一样,派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的初始化;
#include<iostream>
using namespace std;class Person
{
public:Person(const string& name ):_name(name){cout << "Person()" << endl;}Person(const Person& p):_name(p._name){cout << "Person拷贝构造调用" << endl;}protected:string _name;
};class Student:public Person
{
public:Student(const string& name, int num):Person(name),_num(num){cout << "Student()" << endl;}// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student(const Student& s1):Person(s1),_num(s1._num){cout << "Student拷贝构造调用" << endl;}protected:int _num;
};int main()
{Student s1("lwt", 12);Student s2(s1);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
3. 派生类的operator=必须调用基类的operator=完成基类的赋值。但需要注意的是派生类的operator=隐藏了基类的operator=,所以要显示调用基类的operator=。
#include<iostream>
using namespace std;class Person
{
public:Person(const string& name ):_name(name){cout << "Person()" << endl;}Person(const Person& p):_name(p._name){cout << "Person拷贝构造调用" << endl;}Person& operator=(const Person& p){cout << "Person& operator=(const Person& p)" << endl;if (this != &p){_name = p._name;}return *this;}
protected:string _name;
};class Student:public Person
{
public:Student(const string& name, int num):Person(name),_num(num){cout << "Student()" << endl;}// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student(const Student& s1):Person(s1),_num(s1._num){cout << "Student拷贝构造调用" << endl;}void print(){cout << _num << endl;cout << _name << endl;}Student& operator=(const Student& s1){if (this != &s1){//父类和子类的operator=构成隐藏关系Person::operator=(s1);_num = s1._num;}return *this;}protected:int _num;
};int main()
{Student s1("lwt", 12);Student s2(s1);Student s3("lll", 15);cout << endl;s2.print();cout << endl;s2 = s3;s2.print();return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
有一个很巧妙的点就在于,你看在Student内重载operator=那里的Person::operator=(s1),如果我们没有学到基类和派生类之间的转换的话我们不懂为什么Person内重载的operator的参数是Person,而在这里我们传了类型为Student的s1进去,这里就造成了切割或切片。
4. 派生类对象初始化先调用基类构造再调用派生类构造;
#include<iostream>
using namespace std;class Person
{
public:Person(const string& name ):_name(name){cout << "Person()" << endl;}Person(const Person& p):_name(p._name){cout << "Person拷贝构造调用" << endl;}protected:string _name;
};class Student:public Person
{
public:Student(const string& name, int num):Person(name),_num(num){cout << "Student()" << endl;}// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student(const Student& s1):Person(s1),_num(s1._num){cout << "Student拷贝构造调用" << endl;}void print(){cout << _num << endl;cout << _name << endl;}protected:int _num;
};int main()
{Student s1("lwt", 12);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
5. 析构函数的调用,如果说这里面没有动态申请空间的话,默认的析构函数也够用了,如果动态申请了的话我们就需要写析构函数清理申请的内存,那问题又来了,我们不仅要清理派生类里面的空间,还要清理基类内的空间,那么我们就需要写两个析构函数,但是呢问题又来了,基类的析构和派生类的析构构成隐藏,原因是无论析构函数的名字怎么样,最后都会被编译器转为一个名为destructor()的函数,所以会导致隐藏,那么我们就需要指定一下作用域;如下代码所示:
#include<iostream>
using namespace std;
class Person
{
public:Person(const char* name = "胡图图"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};class Student : public Person
{
public:Student(const char* name, int num, const char* addrss):Person(name), _num(num), _addrss(addrss){}// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源,才需要自己实现Student(const Student& s):Person(s), _num(s._num), _addrss(s._addrss){// 深拷贝}// 严格说Student析构默认生成的就够用了// 如果有需要显示释放的资源,才需要自己实现// 析构函数都会被特殊处理成destructor() ~Student(){// 子类的析构和父类析构函数也构成隐藏关系// 规定:不需要显示调用,子类析构函数之后,会自动调用父类析构// 这样保证析构顺序,先子后父,显示调用取决于实现的人,不能保证// 先子后父cout<<"~Student()"<<endl;Person::~Person();delete _ptr;}
protected:int _num = 1; //学号string _addrss = "翻斗花园";int* _ptr = new int[10];
};int main()
{Student* ptr = new Student("胡图图",10,"翻斗花园");delete ptr;return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
这里为什么会有两个~Person呢?原因是其实派生类的析构函数调用后会自动调用基类的析构函数,所以不需要我们写;我们可以把~Student里面的Person::~Person()删掉即可;
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
最后总结一下:一般如果没有深拷贝的话拷贝构造和赋值运算符重载不需要我们自己写,用默认的就行,析构函数也是;然后析构函数的调用顺序是先子后父
5. 实现一个不能被继承的类
方法一:让构造函数的访问限定符为private,这样就无法调用了。
#include<iostream>
using namespace std;class Person
{
public:private:Person(){}
};class Student : public Person
{
public:
};
int main()
{Student s1;return 0;
}
方法二:c++11新加的关键字final,final加到基类的类名后就不能被继承了。
6. 继承和友元
友元的关系不能被继承,说明基类的友元不能访问派生类的私有保护成员;举个很简单的例子方便记忆,“父亲的朋友不是你的朋友”;如下代码所示:
7. 继承与静态成员
基类定义了static静态成员,则整个继承体系只有一个这样的成员,派生出多少个派生类,都只有一个static成员实例;
#include<iostream>
using namespace std;class Person
{
public:string _name;static int _count;};int Person::_count = 0;class Student:public Person
{
protected:int _stuNum;
};int main()
{Student s;Person p;//地址不一样说明已经继承了cout << &s._name << endl;cout << &p._name << endl;//地址一样说明只有一份cout << &s._count << endl;cout << &p._count << endl;return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
7. 多继承以及菱形继承的问题
7.1 继承模型
单继承:一个派生类只有一个基类。
多继承:一个派生类有两个或两个以上的基类,“我爸”有一个Son的基类和Dad的基类,多继承对象在内存中的模型是先继承的基类放在前面,后继承的放后面。
菱形继承:是多继承的一种特殊情况,可能祖师爷那会喝醉了没想到这个情况吧。菱形继承会导致数据冗余和二义性的问题,Assistant里面有两份Person,因为Student里面有一份Person,Teacher里面也有一份Person;
#include<iostream>
using namespace std;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()
{Assistant a;a._name() = "胡图图";
}
如果我们想解决这个问题的话可以指定访问指定的基类成员就可以了;
7.2 虚继承
有了多继承,菱形继承就无法避免最好不要实现出来,但我们也可以在其中做出一些操作;在两个会继承到同一个基类的派生类处加关键字virtual即可;如下代码所示:
#include<iostream>
using namespace std;class Person
{
public:string _name;
};class Student :public virtual Person
{
protected:int _num;
};class Teacher :public virtual Person
{
protected:int _id;
};class Assistant :public Student, public Teacher
{
public:
protected:string _majorCourse;
};int main()
{Assistant a;a._name = "胡图图";//a.Student::_name = "胡图图";//a.Teacher::_name = "胡英俊";return 0;
}
这串代码就可以执行了;而这个东西的原理就是把那些继承的数据,通过虚基表和虚基指针来管理这些共享的数据,从而避免了数据的冗余;简单点理解就是放在一个公共区域共同使用。
8. 继承和组合
继承是子类继承了父类的特性,可以说子类其实就是一个特殊的父类,而组合则是将另一个类当做自己的成员变量进行使用;
继承是is-a的关系,举个例子,例如狗是一个动物,那么狗就是子类,而动物就是父类,而狗有动物的所有特性,即是有个新的类需要一个类的所有特性的时候就可以使用继承。
而组合是has-a,这里也举个例子就是汽车有一个轮子,那么我们有一个class Car{class wheels{};};,即有个类需要某些特定的功能的时候则可以使用组合。组合可以很好地保护类的安全性不破坏封装;
END!
相关文章:

C++进阶-->继承(inheritance)
1. 继承的概念及定义 1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要手段,他允许我们在保证原有类的特性基础上还进行扩展,通过继承产生的类叫做派生类(子类),被继承的类叫做基类&a…...

可视化项目 gis 资源复用思路(cesium)
文章目录 可视化项目 gis 资源复用思路底图、模型替换思路具体操作 可视化项目 gis 资源复用思路 背景: A项目的底图、模型 是现在在做的 B项目所需要的,现在要把 B项目的底图之类的替换成 A系统的 底图、模型替换思路 观察可访问系统的 gis 相关网络请…...

SQL实战测试
SQL实战测试 (请写下 SQL 查询语句,不需要展示结果) 表 a DateSalesCustomerRevenue2019/1/1张三A102019/1/5张三A18 1. **用一条 ** SQL 语句写出每个月,每个销售有多少个客户收入多少 输出结果表头为“月”,“销…...
Java 基础教学:基础语法-变量与常量
变量 变量是程序设计中的基本概念,它用于存储信息,这些信息可以在程序执行过程中被读取和修改。 变量的声明 在Java中,声明变量需要指定变量的数据类型以及变量的名称。数据类型定义了变量可以存储的数据种类(例如整数、浮点数…...

vue3使用element-plus手动更改url后is-active和菜单的focus颜色不同步问题
在实习,给了个需求做个新的ui界面,遇到了一个非常烦人的问题 如下,手动修改url时,is-active和focus颜色不同步 虽然可以直接让el-menu-item:focus为白色能解决这个问题,但是我就是想要有颜色哈哈哈,有些执…...
每天五分钟深度学习框架pytorch:从底层实现一元线性回归模型
本文重点 本节课程我们继续搭建一元线性回归模型,不同的是这里我们不使用pytorch框架已经封装好的一些东西,我们做这个目的是为了更加清楚的看到pytorch搭建模型的本质,为了更好的理解,当然实际中我们还是使用pytorch封装好的一些东西,不要重复造轮子。 模型搭建 #定义…...
编辑器加载与AB包加载组合
解释: 这个 ABResMgr 类是一个资源加载管理器,它用于整合 AB包(Asset Bundle)资源加载和 编辑器模式资源加载。通过这个管理器,可以根据开发环境选择资源加载方式,既支持 运行时使用Asset Bundle加载&…...
【c++】vector中的back()函数
nums.back() 是 C 中 std::vector 类的一个成员函数,用于获取数组(向量)中的最后一个元素。以下是一些关于 nums.back() 的详细解释和示例使用: 1. 功能 nums.back() 返回数组 nums 中的最后一个元素。如果数组为空,…...

[分享] SQL在线编辑工具(好用)
在线SQL编写工具(无广告) - 在线SQL编写工具 - Web SQL - SQL在线编辑格式化 - WGCLOUD...
element-ui隐藏表单必填星号
// 必填星号在前显示 去掉 .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before { content: !important; margin-right: 0px!important; } // 必填星号在结尾显示 .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__labe…...

自动驾驶系列—激光雷达点云数据在自动驾驶场景中的深度应用
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中…...
C#删除dataGridView 选中行
关键在于:从最后一行开始删除。 从前往后删只能删除其中一半,我理解是再remove行的时候dataGridView内部行序列发生了变化,包含在选中行中的特定行会被忽略,从后往前删就可避免这个问题,最后一行的行号影响不到前面的…...

K8S调度不平衡问题分析过程和解决方案
不平衡问题排查 问题描述: 1、业务部署大量pod(据反馈,基本为任务型进程)过程中,k8s node内存使用率表现不均衡,范围从80%到百分之几; 2、单个node内存使用率超过95%,仍未发生pod驱逐,存在node…...

Python中类、继承和方法重写的使用
😀前言 本篇博文将介绍如何定义类、创建类的实例、访问类的成员、使用属性、实现继承及方法重写,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以…...

【Neo4j】- 轻松入门图数据库
文章目录 前言-场景一、Neo4j概述二、软件安装部署1.软件下载2.软件部署3.软件使用4.语法学习 总结 前言-场景 这里用大家都了解的关系数据与图数据据库对比着说,更加方便大家理解图数据库的作用 图形数据库和关系数据库均存储信息并表示数据之间的关系。但是,关系…...
LeetCode 206 - 反转链表
解题思路 我们可以使用迭代的方法来实现链表的反转,这里我们先介绍迭代的方法。迭代的思路是:从头节点开始,依次将节点的next指针进行反转,使得当前节点的next指向其前一个节点,然后依次向后移动指针,直至…...

AI生成大片,Movie Gen 可以生成长视频并配上完美的音效,带给观众更好的观看体验。
之前的文章中已经给大家介绍了一些关于长视频生成相关的技术,AI生成大片已经越来越近了。感兴趣的小伙伴可以点击下面链接阅读~ Movie Gen 的工作原理可以简单理解为两个主要部分:一个是生成视频的模型,另一个是生成音频的模型。首先&#x…...

Flink on yarn模式下,JobManager异常退出问题
这个问题排除了很久,其中更换了Flink版本,也更换了Hadoop版本一直无法解决,JobManager跑着跑着就异常退出了。资源管理器上是提示运行结束,运行状态是被Kill掉。 网上搜了一圈,都说内存不足、资源不足,配置…...

面对AI算力需求激增,如何守护数据中心机房安全?
随着人工智能(AI)技术飞速发展,AI算力需求呈现爆发式增长,导致对数据设备电力的需求指数级攀升。这给数据中心带来前所未有的挑战和机遇,从提供稳定的电力供应、优化高密度的部署,到数据安全的隐私保护&…...

Connection --- 连接管理模块
目录 模块设计 模块实现 shared_from_this 模块测试纠错 模块设计 Connection模块是对通信连接也就是通信套接字的整体的管理模块,对连接的所有操作都是通过这个模块提供的接口来完成的。 那么他具体要进行哪些方面的管理呢? 首先每个通信连接都需…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...