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.pswp.cn/web/73064.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/web/73064.shtml
    英文地址,請注明出處:http://en.pswp.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;本文將為你提供…