当前位置: 首页 > news >正文

【CPP】继承语法详解与菱形继承

关于我:在这里插入图片描述


睡觉待开机:个人主页

个人专栏: 《优选算法》《C语言》《CPP》
生活的理想,就是为了理想的生活!
作者留言

PDF版免费提供倘若有需要,想拿我写的博客进行学习和交流,可以私信我将免费提供PDF版。
留下你的建议倘若你发现本文中的内容和配图有任何错误或改进建议,请直接评论或者私信。
倡导提问与交流关于本文任何不明之处,请及时评论和私信,看到即回复。


参考目录

  • 1.前言
  • 2.回顾封装的概念
  • 3.继承的概念
  • 4.继承的定义
    • 4.1继承的定义格式
    • 4.2继承权限
  • 5.父类和子类对象赋值转换
  • 6.继承种的作用域
  • 7.子类的默认成员函数
    • 7.1构成函数
    • 7.2拷贝构造函数
    • 7.3赋值运算符重载函数
    • 7.4析构函数
  • 8.继承与友元函数
  • 9.继承与静态成员
  • 10.菱形继承
    • 10.1单继承与多继承
    • 10.2多继承中的菱形继承问题及虚拟继承
    • 10.3菱形继承中虚拟继承的原理
    • 10.4在继承公共基类的时候使用虚继承
    • 10.5腰部父类也使用虚继承模型
    • 10.6菱形继承的实例
  • 11.组合与继承


1.前言

我们知道,CPP有三大特性:封装、继承和多态。在本文中,我们将简单回顾一下封装的理解,由浅到深的去了解继承的相关语法和一些高级语法。

好,那话不多说,让我们开始吧。

2.回顾封装的概念

什么是封装呢?
实际上,这是一个CPP概念理解中一个挺重要的概念之一,请你简谈一下对CPP封装语法的理解。

我是这样理解的,以下回答仅供参考:
从语法的角度:就是把数据和函数方法进行合并,并用访问限定符修饰加以限定。
从上下层角度:就是把一个类放到另一个类里面,通过typedef的方式,封装成为一个全新的类型。
这个类型从上层使用来说,可能会与一些其他类型使用保持一致,但是底层差异很大,比如我们之前接触过的deque和vector,deque的底层完全不是一个连续的空间,但是通过封装的方式,使得deque得使用与vector差别不大。再比如说,我们之前使用正向迭代器封装成为反向迭代器,反向迭代器虽然在使用上与与正向迭代器一致,实际上就是给正向迭代器套了一层壳。

好的,以上便是我对封装得一点简单理解。下面我们开始谈继承得相关概念~我先说继承是一种特殊得代码复用得一种形式。
谈到代码复用,我们前面学过一些代码复用简单来回顾一下:

  • 函数逻辑的代码复用
  • 针对类/函数大体逻辑相似,只有类型不同的模板代码复用
  • 继承,是一种类层次设计上的代码复用
    在这里插入图片描述

那我们现在正式进入介绍继承语法这一阶段。

3.继承的概念

继承:类层次设计的一种代码复用。
在这里插入图片描述

如何理解继承的概念呢?举个比较形象的例子,你父亲的东西你可以拿过来用,这就是你继承了你父亲的一些东西。比如说,你长大了,是不是可以继承一些你父亲的财产?你父亲的技术?哈哈(仅仅是举个例子)。代码也是同理,我们可以定义一个父类,然后去让别的类继承,比如说上图中我们下面定义的学生类、老师类、导员类都是可以去继承人类这个类的,继承了有什么好处呢?很多重复的类代码就不用自己去写了呗,本质就是一种代码复用。比如说学生、老师、导员都可以继承人类的名字,这样就不用再在学生类、老师类、导员类中每个中都去定义一个名字变量了对不对。

如此好用的继承,那该如何定义呢?下面来简单进行介绍。

4.继承的定义

4.1继承的定义格式

先来说一下继承的定义格式:
在这里插入图片描述
主要是在一个一般的类声明的基础上在后面跟个冒号,然后写继承方式(public/protected/private),然后再去写明继承父类的名字就好了。

总结下来就是:class 继承子类名称 : 继承方式 继承父类名称

是不是语法设置很简单呢?但是在这里我们提到了继承方式,那啥是继承方式呢?
继承方式指的是继承子类打算以什么方式去继承这个父类的一些成员,继承方式有下面三种:public、private、protected三种继承方式。
除此之外,父类中每个成员都会访问限定符进行修饰。
继承方式和每个成员的访问限定符共同决定了子类中到底继承到的成员具有什么权限。

在这里插入图片描述
继承方式有三种,每个父类成员的访问限定符又有三种,所以组合起来一共有9种情况。情况比较多,待我一一道来。

4.2继承权限

前面提过,子类成员继承成员权限 = 父类成员修饰限定符 + 继承方式共同决定。

我总结了下面表格:
在这里插入图片描述
可能有些同学会对这个表格的一些内容感到不太理解,没关系,下面我挨个说明,挨个去举例。

class F
{
private:void PriTest(){cout << "F:PriTest()" << endl;}
protected:void ProTest(){cout << "F:ProTest()" << endl;}
public:void PubTest(){cout << "F:PubTest()" << endl;}
};class PriS : private F
{
public:void PriSTest_Pri(){//PriTest();//父类私有成员,私有继承,类内不能访问}void PriSTest_Pro(){ProTest();//父类保护成员,私有继承,类内可以访问}void PriSTest_Pub(){PubTest();//父类公共成员,私有继承,类内可以访问}
};
void PriSTest()
{PriS pris;//对于私有继承,所有父类成员均不可在类外访问
}class ProS : protected F
{
public:void ProSTest_Pri(){//PriTest();//父类私有成员,保护继承,类内不能访问}void ProSTest_Pro(){ProTest();//父类保护成员,保护继承,类内可以访问}void ProSTest_Pub(){PubTest();//父类公共成员,保护继承,类内可以访问}
};
void ProSTest()
{ProS pros;//对于保护继承,所有父类成员均不可在类外访问
}class PubS : public F
{
public:void PubSTest_Pri(){//PriTest();//父类私有成员,保护继承,类内不能访问}void PubSTest_Pro(){ProTest();//父类保护成员,保护继承,类内可以访问}void PubSTest_Pub(){PubTest();//父类公共成员,保护继承,类内可以访问}
};
void PubSTest()
{PubS pubs;pubs.PubTest();//对于公共继承,所有父类成员种只有公共成员才可在类外访问
}
void test1()
{PriSTest();ProSTest();PubSTest();
}

可能有些同学还是不太能理解,虽然上面附了一些代码…
那我直接总结了一些规律来供大家快速理解上面表格。

  • 对于保护访问限定符的理解
    protected是针对于CPP继承语法而诞生的。
    protected所修饰的父类成员,允许在子类中使用,但是不允许在子类类外使用。
  • 私有继承和私有成员的理解
    私有继承:继承方式是private的继承,私有成员:被private修饰符所修饰的类成员。
    私有继承对父类的public、protected修饰的成员是可见的。但是任何继承方式对于父类种private修饰的成员是不可见的。
  • 继承访问限定的确定
    对于不是父类私有的成员,我们可以取其继承方式和权限修饰限定符的权限较小者。比如说,继承方式是protected,对于父类中的public成员,那么继承下来的就是protected权限。
  • struct和class默认继承
    其实针对于struct和class继承,是可以进行默认继承的,就是写继承定义语法的时候可以省略继承方式。对于struct,默认继承方式是public继承,对于class,默认就是private继承。

这里我们不妨来做个引申:CPP中struct与class的区别是什么?
struct、class做类,默认是public公开成员的,而class是默认private成员的。
struct、class对于继承来说,struct默认继承是公开继承方式,而class默认继承是私有继承方式。
在这里插入图片描述

5.父类和子类对象赋值转换

CPP中支持把子类对象赋值给父类对象,有个专属的名词叫做切片切割
很新奇吧?为啥其这么个名字呢?
在这里插入图片描述

class Father
{
private:int f_a;
protected:int f_b;
public:int f_c;
};class Son : public Father
{
private:int s_a = 1;
protected:int s_b = 2;
public:int s_c = 3;
};void test2()
{Son s;Father f = s;//代码为 0。
}

在上图种,父类有name、sex、age三个成员变量,子类呢比父类多个_no的变量,
你想,要把一个子类对象强行放到一个父类类型里面,那是不是_no变量会被扔掉?所以十分切合我们所说的这种意思,CPP就形象的称此为“切片”/“切割”啦。

实际上,除了子类对象可以赋给父类对象之外,自然也支持把子类指针给到父类指针,把子类引用给到父类引用啦(请参见下图)。
在这里插入图片描述

void test2()
{Son s;Father f = s;//代码为 0。Father* pf = &s;//代码为 0。Father& qf = s;//代码为 0。
}

除此之外,我还需要介绍:子类给父类对象的时候是没有中间变量产生的
我们都知道,隐式类型转换、强制类型转换都会在赋值中间产生一个临时对象,而子类和福哦类的复制转换是没有临时对象产生的。
这是为什么呢?编译器做了特殊处理。其中的道理我也不太懂,暂且留到以后有机会再说吧哈哈。

之后,我还要去强调另外一点:父类对象不能给到子类类型变量哈。

6.继承种的作用域

两个类构成继承,那么对于作用域而言两者也是相互独立的。
子类和父类中有同名成员不会报错,此时会构成 隐藏
需要主要的是成员函数的隐藏构成条件是函数名一致即可,不需要参数进行比较,两个不同类中的函数不会构成重载哈!只有在同一个作用域的函数才会有重载这一说,我们刚开始就说了两个类有着相互独立的作用域。
我个人建议大家在继承体系定义的时候尽量不要定义重名的成员,因为容易进坑。

class Father2
{
public:int a = 1;void func(){cout << " father " << endl;}
};class Son2 : public Father2
{
public:int a = 2;void func(){cout << " son " << endl;}
};void test3()
{Son2 son2;cout << son2.a << endl;//访问的是son中的变量son2.func();//访问的是son中的函数cout << son2.Father2::a << endl;//访问的是father中的变量son2.Father2::func();//访问的是father中的函数
}

7.子类的默认成员函数

对于子类的默认成员函数认识比较复杂,首先需要对子类的默认成员函数有三个方面进行认识:一整个父类 + 子类中的内置类型 + 子类中的自定义类型
在这里插入图片描述

7.1构成函数

子类构造的逻辑:
如果你不写子类的构造函数,那么编译器帮你自动生成一个默认构造函数,这个默认构造函数会忽略子类中的内置类型,会去自动调用子类中的自定义类型,会去自动调用父类的默认构造函数,如果此时父类没有默认构造函数就会报错哈!

class Fa
{
public:int _fa;
};
class So: public Fa
{
public:int _so;
};void test4()
{So s;//在父类和子类都不写构造的情况下,子类会生成默认构造//子类默认构造里会去调用父类的默认构造
}
class Fa
{
public:int _fa;Fa(int f, char c)//此时Fa没有默认构造函数{}
};
class So : public Fa
{
public:int _so;};void test4()
{So s;//So::So(void)”: 由于 基类“Fa”不具备相应的 默认构造函数 或重载解决不明确,因此已隐式删除函数//此时So不写默认构造,编译器会自动生成子类默认构造函数,并去调用父类的默认构造函数、//但是父类没有默认构造,因而报错
}

什么是默认构造函数?
全缺省的,编译器默认生成的,你显示写的无参的构造函数我们都叫做默认构造函数。

如果你显示写了子类的构造函数,并且都正常去对子类中的内置类型做了处理,也调用了子类中自定义类型的构造函数,指明调用了父类中的构造函数,那么编译器就会按照你写的去走。
但是如果你显示写了子类的构造函数,但是里面什么都没写,那么编译器怎么做呢?此时请注意:编译器依然会对子类内置类型忽略,对子类中的自定义类型去调用对应的构造函数,仍然会调用父类的默认构造。为什么?明明我什么都没有写啊!因为编译器会自动走构造函数的初始化列表!

class Fa
{
public:int _fa;//此时_fa存在默认构造函数
};
class So : public Fa
{
public:int _so;So():_so(1){}
};void test4()
{So s;//此时so写了子类构造函数,会去调用父类默认构造函数。
}
class Fa
{
public:int _fa;Fa(int c):_fa(1){//此时Fa没有默认构造函数}
};
class So : public Fa
{
public:int _so;So():Fa(1)//明确写要调用父类的非默认构造函数,_so(1){}
};void test4()
{So s;//So明确写了构造函数,虽然父类中没有默认构造,但是子类构造明确调用父类有参构造,所以也可以正常运行
}

7.2拷贝构造函数

拷贝构造的逻辑基本与构造函数是一样的,依然编译器会自动给你生成一个。这里就不再多介绍了。

不过有一点我需要强调哈:就是拷贝构造函数与构造函数是并列关系,显示写有参构造不会影响编译器生成拷贝构造函数。但是我写一个拷贝构造函数编译器不再生成默认构造函数了哈。

这个地方比较奇怪,这都怪CPP的老古董语法了~

class father
{
public:int _f = 1;
};class son : public father
{
public:int _s = 1;
};void test5()
{son s;son s2(s);//子类有默认拷贝构造,父类也有,所以这时候是没有问题的
}
class father
{
public:int _f = 1;father() = default;//强制生成默认构造函数//拷贝构造father(father& f):_f(f._f){}
};class son : public father
{
public:int _s = 1;son() = default;//强制生成默认构造函数son(son& s):_s(s._s){cout << " father " << endl;}
};void test5()
{son s2;son s(s2);//father//子类拷贝构造即使不写调用父类拷贝构造,也会去默认调用
}
class son : public father
{
public:int _s = 1;son() = default;//强制生成默认构造函数son(son& s):father(s)//明确写调用父类的拷贝构造,注意这个地方会发生切片,_s(s._s){cout << " father " << endl;}
};void test5()
{son s2;son s(s2);//father//子类拷贝构造写调用父类拷贝构造,那么也会去调用父类的拷贝构造函数
}

7.3赋值运算符重载函数

这个跟上面的构造函数还是不太一样的,需要着重说一下。

如果子类和父类的赋值运算符重载函数自己都不写,编译器都会默认进行生成,对于子类的内置类型,直接浅拷贝(值拷贝),对于自定义类型,那么就直接调用对应的拷贝构造函数,同样对于父类的赋值也自然会去调用。

如果子类中明确写了赋值,但是子类赋值没有写要访问父类赋值,此时并不会去调用父类赋值。为什么跟前面两个拷贝构造、构造不一样呢?因为前两个构造都要走初始化列表,但是赋值函数没有初始化列表这一说。

class F1
{
public:int _f;F1(){_f = 2;cout << "F1()" << endl;}F1& operator=(const F1& f){if (this != &f)//排除自己给自己赋值的情况{cout << "F1& operator=(const F1& f)" << endl;_f = f._f;}return *this;}
};class S1 : public F1
{
public:int _s;S1(){_s = 1;cout << "S1()" << endl;}S1& operator=(const S1& s){if (&s != this){//不写,不去默认调用父类的赋值函数。cout << "S1& operator=(const S1& s)" << endl;_s = s._s;}return *this;}
};void test6()
{S1 s1;//F1() S1()S1 s2;//F1() S1()s2 = s1;//S1& operator=(const S1& s)
}

要显示写调用的话怎么写?前面要加类名限定符。不写的后果就是死递归,然后程序挂掉。

class F1
{
public:int _f;F1(){_f = 2;cout << "F1()" << endl;}F1& operator=(const F1& f){if (this != &f)//排除自己给自己赋值的情况{cout << "F1& operator=(const F1& f)" << endl;_f = f._f;}return *this;}
};class S1 : public F1
{
public:int _s;S1(){_s = 1;cout << "S1()" << endl;}S1& operator=(const S1& s){if (&s != this){F1::operator=(s);//这个地方前面一定要写F1,不然就是死递归cout << "S1& operator=(const S1& s)" << endl;_s = s._s;}return *this;}
};void test6()
{S1 s1;//F1() S1()S1 s2;//F1() S1()s2 = s1;//F1& operator=(const F1& f) S1& operator=(const S1& s)
}

7.4析构函数

子类的析构函数调用结束后会自动调用父类的析构函数。->原因在于要保证先析构子类后析构父类,因为子类是可以访问父类的,如果先析构父类,那么再访问父类的成员会出现意想不到的结果。

class Fa
{
public:int _fa;~Fa(){cout << "~Fa()" << endl;}};
class So : public Fa
{
public:int _so;~So(){cout << "~So()" << endl;}
};void test7()
{So s;//~So()//~Fa()
}

子类和父类的析构函数在子类函数中也会发生隐藏/重定义,写的时候也要前面加上类名->这是因为后面多态的缘故,编译器对析构底层做了特殊处理,使得子类和父类的析构函数产生了隐藏/重定义。

class Fa
{
public:int _fa;~Fa(){cout << "~Fa()" << endl;}};
class So : public Fa
{
public:int _so;~So(){Fa::~Fa();//这个地方前面也得指明类域cout << "~So()" << endl;}
};void test7()
{So s;//~Fa()//~So()//~Fa()
}

8.继承与友元函数

把继承这个新语法加入与友元函数又有什么火花呢?

继承对于友元函数是没什么关系哈,我们如果把友元函数比作是朋友,那么继承就类似于父子之间的关系,你父亲的朋友跟你没啥关系,你的朋友也跟你父亲没啥关系。

class Student;//类的声明
class Person
{
public:friend void Display(const Person& p, const Student& s);protected:string _name = "1"; // 姓名
};class Student : public Person
{
protected:string _s = "2";
};void Display(const Person& p, const Student& s)
{cout << p._name << endl;//友元函数仅可以访问父类的东西//cout << s._stuNum << endl;//报错
}void test81()
{Person p;Student s;Display(p, s);
}

不过需要注意的是,因为你继承了你父亲的一些成员,所以友元函数是可以访问你继承了你父亲这一部分的成员的,因为这些成员是属于你的(加入说函数在该类友元的话)。

class Student;//类的声明
class Person
{
public:
protected:string _name = "1"; // 姓名
};class Student : public Person
{friend void Display(const Person& p, const Student& s);protected:string _s = "2";
};void Display(const Person& p, const Student& s)
{cout << s._name << endl; //友元函数可以访问子类继承父类的东西cout << s._s << endl; //友元函数仅可以访问子类的东西//cout << p._name << endl;//此时去访问父类的东西会报错
}void test81()
{Person p;Student s;Display(p, s);
}

9.继承与静态成员

对于一般的变量,父类对象有一份,继承他的子类对象也有一份(前提是父类变量不是私有的哈)。
对于静态变量比较特殊,CPP规定只有一份,既属于父类,也属于子类。请注意,整个父类无论有多少对象,都只有一个static变量!

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

应用:统计下生成了多少个父类+子类对象

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; // 研究科目
};
void TestPerson()
{Student s1;Student s2;Student s3;Graduate s4;cout << " 人数 :" << Person::_count << endl;Student::_count = 0;cout << " 人数 :" << Person::_count << endl;
}

10.菱形继承

10.1单继承与多继承

在介绍什么是菱形继承之前,我先来说一下什么是单继承与多继承的概念。
继承按照可以继承父类的数量可以分为单继承多继承
单继承:
在这里插入图片描述
多继承:
在这里插入图片描述
前面讲的都是单继承,CPP中也有多继承机制,在多继承机制下,CPP为多种场景提供了更好的支持,但是,多继承中的菱形继承存在一定的小问题!
在这里插入图片描述

10.2多继承中的菱形继承问题及虚拟继承

在这里插入图片描述
在上面菱形继承中,我们发现同一份变量会继承两份。这样会造成数据冗余和二义性问题。

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; // 主修课程
};
void test10()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;//a._name = "peter";// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}

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

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 test10()
{// 这样会有二义性无法明确知道访问的是哪一个Assistant a;a._name = "peter";// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决a.Student::_name = "xxx";a.Teacher::_name = "yyy";cout << a._name << endl;//yyycout << a.Student::_name << endl;//yyycout << a.Teacher::_name << endl;//yyycout << &a._name << endl;//000000860A0FFAA8cout << &a.Student::_name << endl;//000000860A0FFAA8cout << &a.Teacher::_name << endl;//000000860A0FFAA8
}

10.3菱形继承中虚拟继承的原理

虚拟继承是如何解决菱形继承二义性、数据冗余的问题的呢?
为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

//模型代码
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;
};
void test11()
{//这是在没有使用虚拟继承情况下的菱形继承,看d的内存空间D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;
}

在这里插入图片描述

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;
};
void test11()
{//这是在使用虚拟继承情况下的菱形继承,看d的内存空间D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;
}

在这里插入图片描述
在这里插入图片描述
我们发现在d的存储中的确是只有一个_a了,然后多了两个指针,一个是黄色曲线的,一个是蓝色曲线的。
黄色曲线的指针指向了一个00,后面紧跟着一个数字20,这个20代表在d内存中_b到_a之间的偏移量,蓝色同理,代表的是在d内存中_c到_a之间的偏移量。
B区域的开始,0x63C+0x14=0x650,C区域的开始0x644+0x0C=0x650
其中,_a我们称之为虚基类,一般放在最下面,用偏移量进行访问,主要用于切片时候。

菱形继承中的指针:

void test11()
{//这是在使用虚拟继承情况下的菱形继承,看d的内存空间D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;B* pb = &d;C* pc = &d;cout << pb << endl;//00AFF658cout << pc << endl;//00AFF660
}

在这里插入图片描述

需要虚继承的话,那在什么地方加上关键字virtual呢?

10.4在继承公共基类的时候使用虚继承

在这里插入图片描述
在这种情况下,也属于菱形继承,在使用虚拟继承的时候,应该把关键字virtual加到Student、Teacher类上。因为他俩有公共的基类。
在这里插入图片描述

10.5腰部父类也使用虚继承模型

之所以这样,是因为方便指针进行访问。使用了菱形虚拟继承之后,定义一个中间父类的指针,我们发现既可以是子类做切片进行访问,又可以是访问它本身,为了统一处理,CPP干脆把中间的父类模型结构也换成了与子类大体一致的模型。

10.6菱形继承的实例

菱形继承在库中有一个案例,就是iostream,这个地方用到的就是菱形虚拟继承的方式进行处理的。
在这里插入图片描述

11.组合与继承

与继承相似的一种代码复用方式叫做组合。
组合的概念:一个类把另一个类作为他的成员变量。类似于一种包含关系。

class A
{
public:int _a;
};class B
{
public:A _aa;//组合int _b;
};void test13()
{B b;
}

在这里插入图片描述

在这里插入图片描述



好的,如果本篇文章对你有帮助,不妨点个赞~谢谢。
在这里插入图片描述


EOF

相关文章:

【CPP】继承语法详解与菱形继承

关于我&#xff1a; 睡觉待开机&#xff1a;个人主页 个人专栏: 《优选算法》《C语言》《CPP》 生活的理想&#xff0c;就是为了理想的生活! 作者留言 PDF版免费提供&#xff1a;倘若有需要&#xff0c;想拿我写的博客进行学习和交流&#xff0c;可以私信我将免费提供PDF版。…...

数据结构(6.2_1)——领接矩阵法

图的存储——邻接矩阵法 邻接矩阵&#xff08;Adjacency Matrix&#xff09;是一种使用二维数组来表示图的方法。在这种表示法中&#xff0c;矩阵的行和列都对应图的顶点。 特点 对于无向图&#xff0c;如果顶点i与顶点j之间有边&#xff0c;则矩阵的第i行第j列&#xff08;…...

诈骗未成功是否构成犯罪?

诈骗未成功不一定构成犯罪。在刑法上&#xff0c;构成诈骗罪需要满足特定的构成要件&#xff0c;包括有非法占有的目的、实施了虚构事实或隐瞒真相的行为、对方因此陷入错误认识并处分财产、行为人或第三方取得财产、被害人遭受财产损失。如果诈骗行为未能成功&#xff0c;即被…...

网络协议栈应用层的意义(内含思维导图和解析图通俗易懂超易理解)

绪论​&#xff1a; “节省时间的方法就是全力以赴的将所要做的事情完美快速的做完&#xff0c;不留返工重新学习的时间&#xff0c;才能省下时间给其他你认为重要的东西。” 本章主要讲到OSI网络协议栈中的应用层的作用和再次在应用层的角度理解协议的具体意义&#xff0c;以及…...

【NXP-MCXA153】i2c驱动移植

介绍 ‌I2C总线由飞利浦公司开发&#xff0c;是一种串行单工通信总线&#xff0c;它主要用于连接微控制器和其他外围设备并在总线上的器件之间传送信息&#xff08;需要指定设备地址&#xff09;&#xff1b;常见的i2c设备有EEPROM、触摸屏、各种IoT传感器、时钟模块等&#x…...

C++(11)类语法分析(2)

C(10)之类语法分析(2) Author: Once Day Date: 2024年8月17日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客 …...

数字验证每日十问--(3)

深拷贝和浅拷贝的区别&#xff1f; 当只拷贝对象中的成员变量和声明的句柄时&#xff0c;称为浅拷贝。浅拷贝只把对象中的句柄复制了&#xff0c;却没有复制句柄b所指向的对象。这会导致复制后&#xff0c;a2中的句柄b 和 a1 中的句柄b指向同一个对象&#xff0c;如果a2中的句…...

22.给定 n 对括号,实现一个算法生成所有可能的正确匹配的括号组合

22. Generate Parentheses 题目 给定 n 对括号,编写一个函数生成所有可能的正确匹配的括号组合。 例如,当 n = 3 时,可能的组合集合为: ["((()))","(()())","(())()","()(())","()()()" ]题目大意 给出 n 代表生成…...

检测到目标URL存在http host头攻击漏洞

漏洞描述 修复措施 方法一&#xff1a; nginx 的 default_server 指令可以定义默认的 server 去处理一些没有匹配到 server_name 的请求&#xff0c;如果没有显式定义&#xff0c;则会选取第一个定义的 server 作为 default_server。 server { …...

C++奇迹之旅:手写vector模拟实现与你探索vector 容器的核心机制与使用技巧

文章目录 &#x1f4dd;基本框架&#x1f320; 构造和销毁&#x1f309;vector()&#x1f309;vector(const vector& v)&#x1f309;vector(size_t n, const T& value T())&#x1f309;赋值拷贝构造&#xff1a;vector<T>& operator(vector<T> v)&a…...

018、钩子函数 mounted和beforeDestroy、父组件向子组件传递参数 props 的使用

文章目录 1、mounted 和 beforeDestroy1.1、mounted1.2、beforeDestroy 2、父组件向子组件传递参数 props2.1、子组件定义2.2、父组件调用子组件并传参 3、完整例子3.1、父组件 Tags.vue3.2、子组件 TagsMenu.vue3.3、效果图 1、mounted 和 beforeDestroy 1.1、mounted mount…...

xlnt在Windows中的dll,lib生成

前言 花了半天时间想要把xlnt 集成到VS2022 Cmake项目中,以我目前掌握的能力,Cmake语法对于我来说难懂,对于只是使用过Cmake编译MySQL,或是其他lib,dll库的小白来说,不应该为了显示自己能力多么出众,强行去配置一些程序内容。 生活中没有绝对的事情,有舍有得. https://github…...

【网络】私有IP和公网IP的转换——NAT技术

目录 引言 NAT工作机制​编辑 NAT技术的优缺点 优点 缺点 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 公网被子网掩码划分为层状结构&#xff0c;一个公网IP的机器又可以用很多私有IP搭建内网。在日常生活场景中用的都是私有IP&#xff0c;例如手机&#xff0c;…...

java 面试 PDF 资料整理

“尊贵的求知者&#xff0c;作者特此献上精心编纂的Java面试宝典PDF&#xff0c;这份资料凝聚了无数面试精华与实战经验&#xff0c;是通往Java技术殿堂的钥匙。若您渴望在Java编程的求职之路上稳健前行&#xff0c;只需轻轻一点&#xff0c;完成这象征支持与认可的一键三联&am…...

初步认识Linux系统

前言 Linux系统具有许多优点&#xff0c;不仅系统性能稳定&#xff0c;而且是开源软件。其核心防火墙组件性能高效、配置简单&#xff0c;保证了系统的安全。在很多企业网络中&#xff0c;为了追求速度和安全&#xff0c;Linux不仅仅是被网络运维人员当作服务器使用&#xff0c…...

JavaScript AI 编程助手

JavaScript AI 编程助手 引言 随着人工智能技术的飞速发展&#xff0c;编程领域也迎来了前所未有的变革。JavaScript&#xff0c;作为全球最流行的编程语言之一&#xff0c;其与AI的结合为开发者带来了巨大的便利和无限的可能性。本文将探讨JavaScript AI编程助手的定义、功能…...

达梦数据库的系统视图v$datafile

达梦数据库的系统视图v$datafile 达梦数据库的V$DATAFILE 是一个重要的系统视图&#xff0c;提供了有关数据库数据文件的信息。 V$DATAFILE 系统视图 V$DATAFILE 视图用于显示数据库中每一个数据文件的详细信息。通过查询这个视图&#xff0c;数据库管理员可以了解数据文件的…...

Triton/window安装: triton-2.0.0-cp310-cp310-win_amd64.whl文件

下面这个github仓&#xff1a; https://github.com/PrashantSaikia/Triton-for-Windows/tree/main 安装命令也很简单&#xff0c;下载到本地后运行: pip install triton-2.0.0-cp310-cp310-win_amd64.whl...

应急响应-DDOS-典型案例

某单位遭受DDoS攻击事件如下 事件背景 2019年2月17日&#xff0c;某机构门户网站无法访问&#xff0c;网络运维人员称疑似遭受DDoS攻击&#xff0c;请求应急响应工程师协助。 事件处置 应急响应工程师在达到现场后&#xff0c;通过查看流量设备&#xff0c;发现攻击者使用僵…...

JAVA学习之知识补充(下)

六&#xff1a;File类与IO流&#xff1a; 这里给出三种常见的初始化方法&#xff1a; 通过文件路径初始化: File file new File("C:/example/test.txt");这种方法用于创建一个文件对象&#xff0c;该文件对象表示指定路径的文件或目录。例如&#xff1a;File fil…...

qt生成一幅纯马赛克图像

由于项目需要&#xff0c;需生成一幅纯马赛克的图像作为背景&#xff0c;经过多次测试成功&#xff0c;记录下来。 方法一&#xff1a;未优化方法 1、代码&#xff1a; #include <QImage> #include <QDebug> #include <QElapsedTimer>QImage generateMosa…...

python循环——九九乘法表(更加轻松的理解循环结构)

感受 首先&#xff0c;得明确意识到这个问题&#xff0c;就是我的循环结构学的一塌糊涂&#xff0c;完全不能很好的使用这个循环来实现各种九九乘法表达输出&#xff0c;这样的循环结构太差了&#xff0c;还需要我自己找时间来补充一下循环的使用&#xff0c;来拓宽自己的思考方…...

UDS诊断系列之十八故障码的状态掩码

在谈19服务的子功能之前&#xff0c;先说一下故障码&#xff08;DTC&#xff09;的状态掩码是什么。 一、状态掩码 状态掩码由八个状态位构成&#xff0c;客户端利用它向服务器请求与其状态相匹配的DTC信息。当服务器接收到来自客户端的请求时&#xff0c;它会通过过滤匹配的…...

【jvm】直接引用

目录 1. 说明2. 形式3. 特点4. 生成过程5. 作用 1. 说明 1.在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;直接引用&#xff08;Direct Reference&#xff09;是相对于符号引用&#xff08;Symbolic Reference&#xff09;而言的&#xff0c;它是指向内存中实际存在的…...

PythonStudio 控件使用常用方式(二十七)TActionList

PythonStudio是一个极强的开发Python的IDE工具&#xff0c;官网地址是&#xff1a;https://glsite.com/ &#xff0c;在官网可以下载最新版的PythonStudio&#xff0c;同时&#xff0c;在使用PythonStudio时&#xff0c;它也能及时为用户升到最新版本。它使用的是Delphi的控件&…...

PDF 转Word 开源库

1. Apache PDFBox Apache PDFBox 是一个开源的 Java 库&#xff0c;用于创建和操作 PDF 文档。虽然 PDFBox 本身没有直接支持 PDF 转 Word 的功能&#xff0c;但它可以提取 PDF 内容&#xff0c;你可以结合其他方法将这些内容写入 Word。 添加依赖 <dependency><gr…...

Docker - 深入理解Dockerfile中的 RUN, CMD 和 ENTRYPOINT

RUN docker file 中的 RUN 命令相对来教容易理解 RUN 指令用于在构建镜像时执行命令&#xff0c;这些命令会在 Docker 镜像的构建过程中执行。常用于安装软件包、设置环境变量、创建目录等。RUN 指令会在镜像构建中创建新的镜像层&#xff0c;每个 RUN 指令都会创建一个新的镜…...

Python 函数式编程 内置高阶函数及周边【进阶篇 3】推荐

前面我们已经总结并实践了用python获取到了数据。也介绍了python中http网络请求的几种方式&#xff0c;正在学习python开发语言或者对python3知识点生疏需要回顾的请点这里 &#xff0c;本章主要总结了函数式编程及特点 和 python中内置的高阶函数及周边知识&#xff0c;方便自…...

【Rust光年纪】探秘Rust GUI库:从安装配置到API概览

Rust语言GUI库全方位比较&#xff1a;选择适合你的工具 前言 在现代软件开发中&#xff0c;图形用户界面&#xff08;GUI&#xff09;库扮演着至关重要的角色。随着Rust语言的不断发展&#xff0c;越来越多的优秀的GUI库也相继问世&#xff0c;为Rust开发者提供了更多选择。本…...

Element plus部分组件样式覆盖记录

文章目录 一、el-button 样式二、Popconfirm 气泡确认框三、Popover 气泡卡片四、Checkbox 多选框五、Pagination 分页六、Form 表单七、Table 表格 一、el-button 样式 html&#xff1a; <el-button class"com_btn_style">button</el-button>样式覆盖…...

重塑业务生态,Vatee万腾平台:引领行业变革的新引擎

在数字经济浪潮汹涌的今天&#xff0c;传统行业的边界正被不断模糊与重塑&#xff0c;新兴技术如云计算、大数据、人工智能等正以前所未有的速度改变着商业世界的面貌。在这一背景下&#xff0c;Vatee万腾平台应运而生&#xff0c;以其独特的创新模式和强大的技术实力&#xff…...

标准术语和定义中的【架构】应该如何描述

一、参考国家标准和国际标准中对“架构”的描述 &#xff08;1&#xff09;GB/T 8566-2022 国家标准 架构的术语描述&#xff1a;(系统)在其环境中的一些基本概念或性质,体现在其元素关系,以及设计与演进原则中。 &#xff08;2&#xff09;ISO/IEC/IEEE 42010 国际标准 架构的…...

华为鸿蒙Core Vision Kit 骨骼检测技术

鸿蒙Core Vision Kit 是华为鸿蒙系统中的一个图像处理框架&#xff0c;旨在提供各种计算机视觉功能&#xff0c;包括物体检测、人脸识别、文本识别等。骨骼检测是其中的一项功能&#xff0c;主要用于检测和识别人类身体的骨骼结构。 骨骼检测的关键点 骨骼点检测&#xff1a;通…...

Table API SQL系统(内置)函数System (Built-in) Function详解

目录 函数类型 引用函数 函数精确引用 函数模糊引用 函数解析顺序 精确的函数引用 模糊的函数引用 系统函数 标量函数(Scalar Functions) 比较函数(Comparison Functions) 逻辑函数(Logical Functions) 算术函数(Arithmetic Functions) 字符串函数(Strin…...

一键运行RocketMQ5.3和Dashboard

一键运行RocketMQ5.3和Dashboard 目录 一键运行RocketMQ5.3和Dashboard通过Docker Compose 来一键启动运行的容器包括docker-compose.yml文件运行命令启动本地效果查看 参考信息 通过Docker Compose 来一键启动 运行的容器包括 NameServerBrokerProxyDashBoard docker-compo…...

HAL STM32 SG90舵机驱动控制

HAL STM32 SG90舵机驱动控制 &#x1f516;测试对象&#xff1a;STM32F103SG90舵机 &#x1f33c;功能实现&#xff1a;通过串口指令&#xff0c;控制SG90舵机转动到指定角度。 ✨在实际硬件舵机驱动过程中&#xff0c;使用SG90普通舵机空载运转情况下&#xff0c;电流在180mA…...

【Kubernetes】k8s集群图形化管理工具之rancher

目录 一.Rancher概述 1.Rancher简介 2.Rancher与k8s的关系及区别 3.Rancher具有的优势 二.Rancher的安装部署 1.实验准备 2.安装 rancher 3.rancher的浏览器使用 一.Rancher概述 1.Rancher简介 Rancher 是一个开源的企业级多集群 Kubernetes 管理平台&#xff0c;实…...

AI编程系列一1小时完成链家房价爬虫程序

背景 AI编程实在太火&#xff0c;写了很多年的Java&#xff0c;现在Python 和Go 简单好用&#xff0c;今天结合智谱清言快速完成一个程序爬虫程序&#xff0c;没有任何Python 编程经验&#xff0c;只需要会提问&#xff0c;熟悉简单HTML结构即可。未来一定是有业务能力者的福…...

【JavaEE初阶】文件内容的读写—数据流

目录 &#x1f4d5; 引言 &#x1f334; 数据流的概念 &#x1f6a9; 数据流分类 &#x1f333; 字节流的读写 &#x1f6a9; InputStream&#xff08;从文件中读取字节内容) &#x1f6a9; OutputStream&#xff08;向文件中写内容&#xff09; &#x1f384; 字符流的…...

Spring Boot项目中使用Sharding-JDBC实现读写分离

Sharding-JDBC是一个分布式数据库中间件&#xff0c;它不仅支持数据分片&#xff0c;还可以轻松实现数据库的读写分离。下面是如何在Spring Boot项目中集成Sharding-JDBC并实现读写分离的详细步骤&#xff1a; 目录 1. 引入依赖 2. 配置数据源 3. 配置Sharding-JDBC相关参数…...

【网络安全】SSO登录过程实现账户接管

未经许可,不得转载。 文章目录 正文正文 登录页面展示了“使用 SSO 登录”功能: 经分析,单点登录(SSO)系统的身份验证过程如下: 1、启动SSO流程:当用户点击按钮时,浏览器会发送一个GET请求到指定的URL: /idp/auth/mid-oidc?req=[UNIQUE_ID]&redirect_uri=[REDI…...

Admin.NET源码学习(3:LazyCaptcha使用浅析)

Admin.NET项目前端登录页面的验证码图片默认使用动态图&#xff0c;且图形内容为阿拉伯数字运算&#xff08;如下图所示&#xff09;&#xff0c;用户输入正确的计算结果才能正常登录。项目采用LazyCaptcha模块生成验证码及动态图。   在Admin.NET.Core项目中添加了Lazy.Cap…...

在原生未启用kdump的BCLinux 8系列服务器上启用kdump及报错处理

本文记录了在原生未启用kdump的BCLinux 8系列操作系统的服务器上手动启用kdump服务及报错处理的过程。 一、问题描述 BCLinux 8系列操作系统&#xff0c;系统初始化安装时未启用kdump服务&#xff0c;手动启动时报以下“No memory reserved for crash kernel”或“ConditionK…...

Android架构组件中的MVVM

Android架构组件中的MVVM&#xff08;Model-View-ViewModel&#xff09;模式是一种广泛应用的设计模式&#xff0c;它通过将应用程序分为三个主要部分&#xff08;Model、View、ViewModel&#xff09;来分离用户界面和业务逻辑&#xff0c;从而提高代码的可维护性、可扩展性和可…...

走向绿色:能源新选择,未来更美好

当前&#xff0c;全球范围内可再生能源正经历着从辅助能源向核心能源的深刻转型&#xff0c;绿色能源日益渗透至居住、出行、日常应用等多个领域&#xff0c;深刻影响着我们的生活方式&#xff0c;使我们能够更加充分地体验清洁能源所带来的优质生活。 一、绿色能源与“住” …...

鸿蒙装饰器的介绍

State装饰器&#xff0c; State装饰的变量&#xff0c;称为状态变量&#xff0c;与声明式范式中的其他被装饰变量一样&#xff0c;是私有的&#xff0c;只能从组件内部访问&#xff0c;在声明时&#xff0c;必须指定其类型和本地初始化。 Provide装饰器和Consume装饰器&#…...

零基础5分钟上手亚马逊云科技核心云架构知识 - 权限管理最佳实践

简介&#xff1a; 欢迎来到小李哥全新亚马逊云科技AWS云计算知识学习系列&#xff0c;适用于任何无云计算或者亚马逊云科技技术背景的开发者&#xff0c;通过这篇文章大家零基础5分钟就能完全学会亚马逊云科技一个经典的服务开发架构方案。 我会每天介绍一个基于亚马逊云科技…...

[数据库][知识]SQL Server、MySQL 和 Oracle 的默认端口和数据库链接

SQL Server、MySQL 和 Oracle 的默认端口号、连接 URL 和驱动类名。以下是对每个数据库连接信息的简要说明&#xff1a; SQL Server 默认端口号&#xff1a;1433JDBC URL 格式&#xff1a;jdbc:sqlserver://localhost:1433;DatabaseNamedbnameJDBC 驱动类名&#xff1a;com.mic…...

【Unity教程】使用 Animation Rigging实现IK制作程序化的动画

在 Unity 开发中&#xff0c;为角色创建逼真且自适应的动画是提升游戏体验的关键。在本教程中&#xff0c;我们将结合 Animation Rigging 工具和 IK&#xff08;Inverse Kinematics&#xff0c;反向运动学&#xff09;插件来实现程序化的动画。 视频教程可以参考b战大佬的视频 …...

OBS混音器(Mixers)的重要性和配置指南

在进行直播或录制时,音频管理是非常关键的一环,特别是在需要同时处理多个音频源的复杂设置中。OBS Studio提供了强大的音频管理工具,其中“混音器”功能扮演了核心角色。混音器(Mixers)在OBS中用于控制不同音频源的输出路由,允许用户精确控制哪些音源出现在最终的直播或录…...