当前位置: 首页 > 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…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...