C++继承机制:从基础到避坑详细解说

目录

1.继承的概念及定义

1.1继承的概念

1.2 继承定义

1.2.1定义格式

1.2.2继承关系和访问限定符

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

总结:

2.基类和派生类对象赋值转换

3.继承中的作用域

4.派生类的默认成员函数

​编辑

默认构造与传参构造

拷贝构造:

显示深拷贝的写法:

赋值

显式调用赋值:

析构函数

​编辑

5.继承和友元:

6.继承与静态成员

7.复杂的菱形继承及菱形虚拟继承

虚拟继承

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

8.继承的总结和反思


总结:C++继承机制详解

1. 继承基础
  • 概念:继承是面向对象的重要特性,允许派生类在复用基类特性的基础上进行扩展,形成层次结构。

  • 访问权限

    • public继承:基类public→派生类publicprotectedprotected

    • protected/private继承:基类成员在派生类中的访问权限会被进一步限制。

    • 基类private成员对所有继承方式不可见,需通过基类提供的接口访问。

  • 默认继承方式

    • class默认private继承,struct默认public继承(但建议显式声明)。

2. 对象赋值与转换
  • 切片(切割):派生类对象可赋值给基类对象、指针或引用(基类仅保留派生类中与自身匹配的部分)。

  • 反向不成立:基类对象不能直接赋值给派生类对象(需强制类型转换,且需确保安全)。

3. 作用域与成员隐藏
  • 独立作用域:基类与派生类作用域独立,同名成员会引发隐藏(重定义)

  • 解决方法:通过基类::成员名显式访问基类成员。

  • 函数隐藏:仅函数名相同即构成隐藏(与参数无关)。

4. 派生类默认成员函数
  • 构造与析构

    • 派生类构造函数需显式调用基类构造函数(若基类无默认构造)。

    • 析构顺序:先派生类后基类(编译器自动调用基类析构,无需显式)。

  • 拷贝与赋值

    • 默认调用基类的拷贝构造和赋值运算符,需显式处理深拷贝问题。

    • 赋值运算符需避免递归调用(通过基类::operator=)。

5. 继承与友元/静态成员
  • 友元不可继承:基类友元无法访问派生类私有/保护成员。

  • 静态成员共享:基类静态成员在整个继承体系中唯一,所有派生类共用。

6. 菱形继承与虚拟继承
  • 问题:菱形继承导致数据冗余二义性(如Assistant对象包含两份Person成员)。

  • 解决:使用虚拟继承virtual),使公共基类(如Person)在派生类中仅保留一份。

  • 原理:通过虚基表指针和偏移量定位唯一基类成员,解决冗余和二义性。

7. 继承 vs. 组合
  • 继承(is-a):适合逻辑上的“派生关系”(如学生是人)。

    • 缺点:高耦合,破坏封装,基类改动影响派生类。

  • 组合(has-a):适合“包含关系”(如汽车有轮胎)。

    • 优点:低耦合,易于维护,优先使用。

8. 关键总结
  • 避免菱形继承:复杂且性能开销大,优先用组合替代多继承。

  • 虚拟继承慎用:仅解决菱形继承问题,其他场景不推荐。

  • 多继承限制:C++支持但易引发问题,Java等语言已弃用。

核心思想:合理使用继承,优先选择组合;理解作用域、权限和对象模型,避免设计复杂继承结构。

1.继承的概念及定义

1.1继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用

#include<iostream>
using namespace std;class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter"; // 姓名
private:int _age = 18; // 年龄
};class Student : public Person 
{
public:void func(){cout << _name << endl;//不能直接访问父类的私有成员,就像不能直接使用爸爸的私房钱一样//cout << _age << endl;//可以间接使用父类的函数访问到父类的私有成员Print();}
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};int main()
{Student s;s.Print();//s._name += 'x';return 0;
}

1.2 继承定义

1.2.1定义格式

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类。

1.2.2继承关系和访问限定符
 

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

类成员/继承方式public继承protected继承private继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成在派生类中不可见在派生类中不可见在派生类中不可

总结:

1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

我们不能直接访问父类的私有成员:

2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected

> private。

比如说上面的代码,Print是基类的公有成员函数,派生类Student类是公有继承,因此对于Student类来说Print仍然是一个可直接访问到的公有函数:这个代码是完全可以运行的。

Student s;
s.Print();

4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过

最好显示的写出继承方式。· 

5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡

使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里

面使用,实际中扩展维护性不强。

补充:

struct默认继承方式和访问限定符都是公有

class默认继承方式和访问限定符都是私有

2.基类和派生类对象赋值转换

  • 派生类对象 可以赋值 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。(ps:这个我们后面再讲解,这里先了解一下)
	int i = 1;double t = i;const string& s = "11111";不相关的类型不能转换//string str = i;//强行转换也不行//string str = (string)i;

相关类型支持隐式类型转换,单参数构造函数支持隐式类型转换,类型转换中间会产生临时变量,临时变量具有常性,因此给引用(引用的是临时变量)的时候必须加const。相关的类型,不可以隐式类型转换,也不可以强转。(指针是地址本质就是整形,因此可以和整形转换)

公有继承的时候,子类能转为父类

保护和私有继承的时候,子类不能转为父类:

public继承:每个子类对象都是一个特殊的父类对象:is a关系

为什么叫切割/切片 : 将子类中和父类一样的部分切出来依次赋值(拷贝)给父类

什么叫做赋值兼容(编译器进行了特殊处理):中间不会产生临时对象

证明:

	//子类能转为父类Student st;Person p = st;//赋值兼容Person& ref = st;Person* ref = &st;

如果是给予一个引用,ref就变成子类对象当中切割出来的父类那一部分的别名。

如果是指针,ptr就指向了子类对象当中切割出来的父类对象的一部分

 

由此可以知道,父类转换成子类是不可以的,因为子类有的父类没有(指针和引用在一些特殊情况可以)

局部域、全局域、命名空间域、类域

局部域和全局域会影响访问(同一个域类不能有同名成员(特殊情况:同名函数构成重载,但函数不构成重载的时候不能同名))和生命周期,类域和命名空间域不影响生命周期只影响访问

3.继承中的作用域

类域的细分:

  •  在继承体系中基类派生类都有独立的作用域。(可以有同名成员)
  • 子类和父类中有同名成员子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类 成员 显示访问)

想访问父类的需要加域访问限定符,指定类域就可以了

  • 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  • 注意在实际中在继承体系里面最好不要定义同名的成员。

请看下题:以下程序的结果是什么?

A. 编译报错        B. 运行报错        C. 两个func构成重载        D. 两个func构成隐藏

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
void Test() 
{B b;b.fun(10);
}

解答:

B中的fun和A中的fun不是构成重载,因为不是在同一作用域
B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。

这样就是错误的

4.派生类的默认成员函数

    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& operator=(const Person & p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
    protected:string _name; // 姓名
    };
    class Student : public Person
    {
    public:protected:int _num; //学号string _str;//将父类成员当成一个整体(或者一个整体自定义类型)//将自己的成员当成一个整体//a、内置类型//b、自定义类型
    };int main()
    {Student s;return 0;
    }

    默认构造与传参构造

    对于派生类,没有写默认构造函数,那在实例化对象的时候是怎么实例化的?

    走的基类的默认构造函数

    在派生类写了默认构造函数:仍然会报错

    因为需要将父类成员当成一个整体,不允许单独对父类的成员进行初始化,需要像匿名对象一样来调用(父类有几个成员就一起传过去):

    class Student : public Person
    {
    public:Student(int num, const char* str, const char* name):Person(name),_num(),_str(){}protected:int _num; //学号string _str;
    };

    父类 + 自己,父类的调用父类构造函数初始化Person(name),这里就等于去初始化,显式的去调用父类的构造,父类的部分只能由父类初始化。

    可以看一个不是派生类的感受一下:派生类就相当于将父类当成一个整体(不能单独初始化)

    拷贝构造:

    如果派生类不写默认拷贝构造会发生什么?调用父类的拷贝构造

    	Student s1(1, "vv", "pupu");Student s2(s1);

    运行结果:对内置类型采用值拷贝,自定义类型采用他的拷贝构造,父类会去调用父类的拷贝构造。

    派生类什么时候需要显式的写他自己的拷贝构造?

    如果他存在深拷贝:(有个指针指向某个资源)

    显示深拷贝的写法:

    	Student(const Student& s):Person(s),_num(s._num),_str(s._str){}

    为什么直接传s就可以?

    父类的拷贝构造:

    Person(const Person& p): _name(p._name)
    {cout << "Person(const Person& p)" << endl;
    }

    赋值

    没有写派生类的赋值构造函数怎么办?调用父类的赋值函数

    显式调用赋值:

    Student& operator=(const Student& s)
    {if (this != &s)//先判断是否是自己给自己赋值{operator=(s); //显示调用运算符重载_num = s._num;_str = s._str;}return *this;
    }

    上面的写法会引起栈溢出: 

    派生类的赋值,和父类的赋值构成隐藏(同名函数),因此会调用自己的赋值,因此这里就死循环了,导致了栈溢出

    指定作用域:

    	Student& operator = (const Student& s){if (this != &s)//先判断是否是自己给自己赋值{Person::operator=(s); //显示调用运算符重载,发生了栈溢出,派生类的赋值,和父类的赋值构成隐藏(同名函数)_num = s._num;_str = s._str;}return *this;}

    析构函数

    一个类的析构函数是否能显示调用:可以

    	Person p("wakak");p.~Person();

    派生类如何显式调用:

    	~Student(){~Person();cout << "~Student()" << endl;}

    这是因为:子类的析构也会隐藏父类,因为后续多态的需要,析构函数名字会被统一处理成destructor(析构函数),因此解决办法同赋值:

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

    但是此时却是这样的(在Student构造函数代码中添加了打印Student()): 析构函数多了一次

    当子类析构调用结束了以后,又自动调用了一次父类的析构,因为析构函数是自动调用的。

    构造需要先保证先父后子,析构需要保证先子后父

    更重要的原因:

    因为在子类的析构函数里面还需要还会访问到父类的成员,不能一上来就析构父类就访问不到了:

    ~Student()
    {Person::~Person();cout << _name << endl;cout << "~Student()" << endl;
    }

    显示写无法保证先子后父

    为了析构顺序是先子后父,子类析构函数结束后,会自动调用父类析构

    运行结果:

    总结:

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

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

    5.继承和友元:

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

    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; //这里的_stuNum是子类的保护成员,基类的友元函数不能访问子类的保护和私有成员
    }
    void main()
    {Person p;Student s;Display(p, s);
    }

    解决方法:

    6.继承与静态成员

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

    class Person
    {
    public:Person() { ++_count; }Person(const Person& p) { ++_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; // 研究科目
    };
    int main()
    {Student s1;Student s2;Student s3;Graduate s4;cout << &Person::_count << endl;Student::_count = 0;cout << &Student::_count << endl;
    }
    

    派生类以及Person一共实例化了多少个:

    	Person() { ++_count; }Person(const Person& p) { ++_count; }

    7.复杂的菱形继承及菱形虚拟继承


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

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

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

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

    会导致的问题:比如说刚才的想计算派生类以及Person一共实例化了多少个,这里实例化一次Assistant就会使count++两次

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

    虚拟继承

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

    virtual加在直接继承公共基类的派生类:

    相当于只有一份a,Student和Teacher共用

    using namespace std;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 Test()
    {Assistant a;a._name = "peter";
    }

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

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

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

    使用内存窗口来观察原理:

    1.菱形继承对象模型

    2.菱形虚拟继承对象模型

    int main()
    {D d;d.B::_a = 1;d.C::_a = 2;d._a = 0;d._b = 3;d._c = 4;d._d = 5;return 0;
    }

    a不再在BC内部,BC多出来的地址(指针)存0,且多出来地址的下一个指针所存的为他们到A的偏移量。因此需要找到A就用自己的地址再加偏移量。(为了切片用A)

    注意pc指向的是中间位置:

    对象模型当中,在内存中的存储是按照声明的顺序。

    ostream也是一个菱形继承。

    总结:实践中可以设计多继承,但是切记不要设计菱形继承,因为太复杂,容易出各种问题。

    虚拟继承后,对象模型都需要保持一致,无论这个指针指向哪,都是使用这个指针找到偏移量,找到a的位置。

    8.继承的总结和反思

    1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

    2. 多继承可以认为是C++的缺陷之一,很多后来的许多语言都没有多继承,如Java

    3. 继承和组合

    • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
    • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
    • 优先使用对象组合,而不是类继承 。
    • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。

    内部细节可见:白,内部细节不可见:黑

            高耦合(模块(类与类之间)之间的关系很紧密,关联很密切,要改动一个地方,所有地方都需要改变,需要重构)

            如何开发设计软件更好:低耦合(方便维护),高内聚(当前类里面的成员关系紧密)

    • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
    • 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合

    简单来说怎么选择使用继承还是组合:

    • 适合is - a 的关系就用继承
    • 适合has - a的关系就用组合
    • 优先使用组合

    经典的is - a问题:

    • 人 --- 学生: 学生是人,而不是学生有一个人
    • 动物 --- 狗: 狗是动物,而不是狗里有一个动物

    经典的has - a问题:

    • 轮胎 --- 汽车:汽车有轮胎,而不是汽车是轮胎

    两者都可以使用:

    • 链表 --- 栈: 栈里有链表,栈是特殊的链表

    1.将A作为B的成员变量(组合也相当于一种复用

    class A
    {
    public:int _a;
    };
    //组合:将A作为B的成员变量
    class B 
    {
    public:A _aa;int _b;
    };
    

    9.笔试面试题

    1. 什么是菱形继承?菱形继承的问题是什么?

    2. 什么是菱形虚拟继承?底层角度是如何解决数据冗余和二义性的

    3. 继承和组合的区别?什么时候用继承?什么时候用组合

    4.C++有多继承?为什么?JAVA为什么没有

    5.多继承问题是什么?本身没啥问题,有多继承就一定会可能写出菱形继承

    结语:

           随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。  

             在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。               

            你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

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

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

    相关文章

    测试基础入门

    文章目录 软件测试基础1.1软件测试概述什么是软件测试什么是软件需求说明书软件测试的原则测试用例的设计测试用例设计的基本原则软件测试分类软件缺陷的定义 2.1软件开发模型软件开发模型概述大爆炸模型&#xff08;边写边改&#xff09;软件开发生命周期模型--螺旋模型软件开…

    022-spdlog

    spdlog 以下是从原理到代码实现的全方位spdlog技术调研结果&#xff0c;结合核心架构、优化策略和完整代码示例&#xff1a; 一、核心架构设计原理 spdlog三级架构 &#xff08;图示说明&#xff1a;spdlog采用三级结构实现日志系统解耦&#xff09; Registry管理中枢 全局…

    STM32时钟树

    时钟树 时钟树就是STM32中用来产生和配置时钟&#xff0c;并且把配置好的时钟发送到各个外设的系统&#xff0c;时钟是所有外设运行的基础&#xff0c;所以时钟也是最先需要配置的东西&#xff0c;在程序中主函数之前还会执行一个SystemClock_Config()函数&#xff0c;这个函数…

    【第22节】windows网络编程模型(WSAAsyncSelect模型)

    目录 引言 一、WSAAsyncSelect模型概述 二、WSAAsyncSelect模型流程 2.1 自定义消息 2.2 创建窗口例程 2.3 初始化套接字 2.4 注册网络事件 2.5 绑定和监听 2.6 消息循环 三、完整示例代码 引言 在网络编程的广袤天地中&#xff0c;高效处理网络事件是构建稳定应用的…

    利用Dify编制用户问题意图识别和规范化回复

    继上一篇文章&#xff0c;成功完成Dify本地部署后&#xff0c;主要做了一些workflow和Agent的应用实现&#xff0c;整体感觉dify在工作流可视化编排方面非常好&#xff0c;即使部分功能无法实现&#xff0c;也可以通过代码执行模块或者自定义工具来实现&#xff08;后续再具体分…

    双核锁步技术在汽车芯片软错误防护中的应用详解

    摘要 本文深入探讨了双核锁步技术在保障汽车芯片安全性中的应用。文章首先分析了国产车规芯片在高安全可靠领域面临的软错误难点及攻克方向&#xff0c;然后详细介绍了双核锁步技术的基本原理及其在汽车芯片防软错误的重要性。通过对比国内外多家厂商的芯片技术&#xff0c;分析…

    Lustre 语言的 Rust 生成相关的工作

    目前 Lustre V6 编译器支持编译生成的语言为C语言。但也注意到&#xff0c;以 Rust 语言为生成目标语言&#xff0c;也存在若干相关工作。 rustre&#xff08;elegaanz&#xff09; 该项工作为 Lustre v6 语言的解析器&#xff0c;使用 Rust 语言实现。生成 Lustre AST。 项…

    Java 之「单调栈」:从入门到实战

    Java 单调栈&#xff1a;从入门到实战 文章目录 Java 单调栈&#xff1a;从入门到实战引言什么是单调栈&#xff1f;单调递增栈单调递减栈 单调栈的应用场景Java 实现单调栈代码示例&#xff1a;下一个更大元素代码解析 单调栈的优势实战应用&#xff1a;股票价格跨度代码示例代…

    【Golang】defer与recover的组合使用

    在Go语言中&#xff0c;defer和recover是两个关键特性&#xff0c;通常结合使用以处理资源管理和异常恢复。以下是它们的核心应用场景及使用示例&#xff1a; 1. defer 的应用场景 defer用于延迟执行函数调用&#xff0c;确保在函数退出前执行特定操作。主要用途包括&#xff…

    CSS 中flex - grow、flex - shrink和flex - basis属性的含义及它们在弹性盒布局中的协同作用。

    大白话CSS 中flex - grow、flex - shrink和flex - basis属性的含义及它们在弹性盒布局中的协同作用。 在 CSS 的弹性盒布局&#xff08;Flexbox&#xff09;里&#xff0c;flex-grow、flex-shrink 和 flex-basis 这三个属性对弹性元素的尺寸和伸缩性起着关键作用。下面为你详细…

    OpenGL ES ->乒乓缓冲,计算只用两个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap

    乒乓缓冲核心思想 不使用乒乓缓冲&#xff0c;如果要每个滤镜作用下的绘制内容&#xff0c;也就是这个滤镜作用下的帧缓冲&#xff0c;需要创建一个Frame Buffer Object加上对应的Frame Buffer Object Texture使用乒乓缓冲&#xff0c;只用两个Frame Buffer Object加上对应的F…

    【HarmonyOS NEXT】关键资产存储开发案例

    在 iOS 开发中 Keychain 是一个非常安全的存储系统&#xff0c;用于保存敏感信息&#xff0c;如密码、证书、密钥等。与文件系统不同&#xff0c;Keychain 提供了更高的安全性&#xff0c;因为它对数据进行了加密&#xff0c;并且只有经过授权的应用程序才能访问存储的数据。那…

    ccfcsp1901线性分类器

    //线性分类器 #include<iostream> using namespace std; int main(){int n,m;cin>>n>>m;int x[1000],y[1000];char z[1000];for(int i0;i<n;i){cin>>x[i]>>y[i];cin>>z[i];}int a[20],b[20],c[20];for(int i0;i<m;i){cin>>a[i…

    Spring Boot 整合 OpenFeign 教程

    精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Spring Boot 整合 OpenFeign 教程 一、OpenFeign 简介 OpenFeign 是 Netflix 开源的声明式 HTTP 客户端&#xff0c;通过接口和注解简化服务间 HTTP 调用。…

    APM 仿真遥控指南

    地面站开发了一段时间了&#xff0c;由于没有硬件&#xff0c;所以一直在 APM 模拟器中验证。我们已经实现了 MAVLink 消息接收和解析&#xff0c;显示无人机状态&#xff0c;给无人机发送消息&#xff0c;实现一键起飞&#xff0c;飞往指定地点&#xff0c;降落&#xff0c;返…

    C语言入门教程100讲(4)输入输出

    文章目录 1. 什么是输入输出&#xff1f;2. 标准输入输出函数2.1 printf 函数2.2 scanf 函数 3. 格式化占位符4. 示例代码代码解析&#xff1a;输出结果&#xff1a; 5. 常见问题问题 1&#xff1a;scanf 中的 & 是什么作用&#xff1f;问题 2&#xff1a;printf 和 scanf …

    《信息系统安全》(第一次上机实验报告)

    实验一 &#xff1a;网络协议分析工具Wireshark 一 实验目的 学习使用网络协议分析工具Wireshark的方法&#xff0c;并用它来分析一些协议。 二实验原理 TCP/IP协议族中网络层、传输层、应用层相关重要协议原理。网络协议分析工具Wireshark的工作原理和基本使用规则。 三 实…

    城市街拍人像自拍电影风格Lr调色教程,手机滤镜PS+Lightroom预设下载!

    调色教程 城市街拍人像自拍的电影风格 Lr 调色&#xff0c;是利用 Adobe Lightroom 软件&#xff0c;对在城市街景中拍摄的人像自拍照片进行后期处理&#xff0c;使其呈现出电影画面般独特的视觉质感与艺术氛围。通过一系列调色操作&#xff0c;改变照片的色彩、明暗、对比等元…

    自学Python创建强大AI:从入门到实现DeepSeek级别的AI

    人工智能&#xff08;AI&#xff09;是当今科技领域最热门的方向之一&#xff0c;而Python是AI开发的首选语言。无论是机器学习、深度学习还是自然语言处理&#xff0c;Python都提供了丰富的库和工具。如果你梦想创建一个像DeepSeek这样强大的AI系统&#xff0c;本文将为你提供…