繼承與組合:C++面向對象的核心

C++ 繼承:從基礎到實戰,徹底搞懂面向對象的 “代碼復用術”

在面向對象編程(OOP)的世界里,“繼承” 是實現代碼復用的核心機制 —— 就像現實中孩子會繼承父母的特征,C++ 的子類也能 “繼承” 父類的成員(變量 + 函數),再添加自己的獨特功能。對于剛接觸 OOP 的開發者來說,繼承既是 “利器”,也藏著不少容易踩坑的細節(比如菱形繼承、隱藏與重載的區別)。

這篇文章會從 “概念→實戰→避坑” 逐步拆解 C++ 繼承,用通俗的語言 + 完整代碼示例,幫你徹底掌握這一知識點,甚至應對筆試面試中的高頻問題。

一、繼承的基礎:什么是繼承?怎么用?

1.1 先搞懂:繼承的 “本質” 是什么?

繼承的核心是 **“復用已有類的代碼,擴展新功能”**。

比如我們有一個Person類(包含姓名、年齡和打印信息的函數),現在要定義StudentTeacher類 —— 這兩個類都需要 “姓名、年齡”,沒必要重復寫,直接 “繼承”Person即可,再補充自己的獨特成員(如學號、工號)。

看代碼更直觀:

// 父類(基類):Person
class Person {
public:// 父類的成員函數:復用給子類void Print() {cout << "姓名:" << _name << endl;cout << "年齡:" << _age << endl;}protected:// 父類的成員變量:復用給子類string _name = "peter";  // 姓名int _age = 18;           // 年齡
};// 子類(派生類):Student,繼承自Person
class Student : public Person {
protected:int _stuid;  // 子類新增的成員:學號
};// 子類(派生類):Teacher,繼承自Person
class Teacher : public Person {
protected:int _jobid;  // 子類新增的成員:工號
};// 測試:子類能直接用父類的Print函數
int main() {Student s;Teacher t;s.Print();  // 輸出:姓名:peter,年齡:18(復用Person的Print)t.Print();  // 同樣復用,無需重復寫代碼return 0;
}

1.2 繼承的 “語法規則”:3 個關鍵要素

要正確使用繼承,必須掌握「繼承方式」和「訪問限定符」的搭配 —— 這決定了父類成員在子類中的 “訪問權限”。

(1)基本語法格式
class 子類名 : 繼承方式 父類名 {// 子類的成員(新增/重定義)
};
  • 父類(基類):被繼承的已有類(如Person);

  • 子類(派生類):新定義的類(如Student);

  • 繼承方式:public(公有的)、protected(保護的)、private(私有的),默認繼承方式:classprivatestructpublic(建議顯式寫清,避免混淆)。

(2)訪問權限的 “黃金表格”

父類成員(public/protected/private)在子類中的權限,由「父類訪問限定符」和「繼承方式」共同決定,核心規則是:子類訪問權限 = min (父類訪問限定符,繼承方式)(優先級:public > protected > private)。

直接看表格更清晰:

父類成員類型public 繼承protected 繼承private 繼承
public 成員子類 public子類 protected子類 private
protected 成員子類 protected子類 protected子類 private
private 成員子類中不可見子類中不可見子類中不可見
(3)3 個必須記住的結論
  1. 父類 private 成員永遠 “不可見”:不是沒繼承,而是語法禁止子類(無論類內還是類外)訪問,相當于 “繼承了但用不了”;

  2. protected 是為繼承設計的:如果父類成員不想被類外訪問,但想讓子類用,就定義為protected(這是protectedprivate的核心區別);

  3. 實際開發優先用 public 繼承protected/private繼承的子類成員只能在類內用,擴展維護性差,幾乎不用。

二、基類與派生類:對象的 “賦值轉換” 規則

子類對象和父類對象之間能不能互相賦值?這里有個形象的說法叫 **“切片”(切割)** —— 把子類中 “屬于父類的部分” 切下來,賦值給父類對象 / 指針 / 引用。

2.1 允許的轉換:子類 → 父類(切片)

子類對象可以直接賦值給父類的對象、指針、引用,無需強制轉換:

class Person {
protected:string _name;  // 姓名int _age;      // 年齡
};class Student : public Person {
public:int _stuid;    // 學號
};void Test() {Student sobj;  // 子類對象// 1. 子類對象 → 父類對象(切片:只賦值父類部分)Person pobj = sobj;// 2. 子類對象地址 → 父類指針(指向子類的父類部分)Person* pp = &sobj;// 3. 子類對象 → 父類引用(引用子類的父類部分)Person& rp = sobj;
}

2.2 禁止的轉換:父類 → 子類

父類對象不能直接賦值給子類對象 —— 因為子類比父類多了成員(如_stuid),父類沒有這部分數據,無法填充子類的新增成員,語法直接禁止:

void Test() {Person pobj;Student sobj;// sobj = pobj;  // 報錯:父類不能賦值給子類
}

2.3 危險的轉換:父類指針 → 子類指針

父類指針可以通過強制轉換賦值給子類指針,但只有一種情況安全:父類指針原本指向的是子類對象(此時指針實際指向的是子類的父類部分,強制轉換后能訪問子類新增成員)。

如果父類指針指向的是父類對象,強制轉換后訪問子類成員會導致越界訪問(父類對象沒有子類成員的內存),非常危險:

void Test() {Student sobj;Person pobj;Person* pp;// 情況1:父類指針指向子類對象 → 強制轉換安全pp = &sobj;Student* ps1 = (Student*)pp;ps1->_stuid = 10;  // 安全:pp實際指向子類,有_stuid內存// 情況2:父類指針指向父類對象 → 強制轉換危險(越界)pp = &pobj;Student* ps2 = (Student*)pp;ps2->_stuid = 10;  // 危險:pobj沒有_stuid,越界訪問內存
}

三、繼承中的 “作用域”:小心 “隱藏” 陷阱

基類和子類是獨立的作用域,這會導致一個常見問題:隱藏(重定義) —— 子類和父類有同名成員時,子類成員會 “屏蔽” 父類成員的直接訪問。

3.1 成員變量的隱藏

子類和父類有同名成員變量時,子類中直接訪問該變量,默認是子類的,父類的需要用父類名::顯式訪問:

class Person {
protected:string _name = "小李子";int _num = 111;  // 父類:身份證號
};class Student : public Person {
public:void Print() {cout << "姓名:" << _name << endl;          // 子類繼承的_namecout << "身份證號:" << Person::_num << endl;// 顯式訪問父類_numcout << "學號:" << _num << endl;            // 子類自己的_num}
protected:int _num = 999;  // 子類:學號(與父類_num同名,隱藏父類)
};void Test() {Student s1;s1.Print();  // 輸出:姓名:小李子;身份證號:111;學號:999
}

3.2 成員函數的隱藏(易混淆點)

成員函數的隱藏規則更 “嚴格”:只要函數名相同,就構成隱藏,不管參數列表、返回值是否相同(這和 “重載” 完全不同 —— 重載要求同一作用域、參數列表不同)。

比如父類Afun(),子類Bfun(int),這兩個函數是隱藏關系,不是重載:

class A {
public:void fun() {cout << "fun()" << endl;}
};class B : public A {
public:// 函數名相同,構成隱藏(不管參數)void fun(int i) {A::fun();  // 顯式訪問父類fun()cout << "fun(int i) → " << i << endl;}
};void Test() {B b;b.fun(10);  // 調用子類fun(int),輸出:fun();fun(int i) → 10// b.fun();  // 報錯:父類fun()被隱藏,需用A::fun()訪問
}

3.3 避坑建議

實際開發中,永遠不要在繼承體系中定義同名成員—— 隱藏會導致代碼可讀性差、容易誤調用,排查 bug 成本高。

四、派生類的 “默認成員函數”:規則要記牢

C++ 類有 6 個默認成員函數(編譯器會自動生成的函數),但派生類的默認成員函數有特殊規則 ——必須先初始化 / 清理父類部分,再處理子類部分

重點關注 4 個核心函數:構造、拷貝構造、賦值重載、析構(取地址重載幾乎不用,忽略)。

4.1 派生類的構造函數

  • 規則:派生類構造函數必須調用父類構造函數,初始化父類部分;

  • 特殊情況:如果父類沒有 “默認構造函數”(無參、全缺省),必須在派生類構造函數的初始化列表中顯式調用父類構造函數。

示例:

class Person {
public:// 父類:帶參構造(無默認構造)Person(const char* name) : _name(name) {cout << "Person(const char*)" << endl;}
protected:string _name;
};class Student : public Person {
public:// 子類構造:必須在初始化列表顯式調用父類構造Student(const char* name, int stuid) : Person(name)  // 先初始化父類, _stuid(stuid) // 再初始化子類{cout << "Student(const char*, int)" << endl;}
protected:int _stuid;
};void Test() {Student s("jack", 1001); // 輸出順序:Person(const char*) → Student(const char*, int)
}

4.2 派生類的拷貝構造函數

  • 規則:派生類拷貝構造必須調用父類拷貝構造函數,拷貝父類部分的數據;

  • 注意:默認生成的派生類拷貝構造會自動調用父類拷貝構造,但如果自己實現,必須顯式調用。

示例:

class Person {
public:Person(const Person& p) : _name(p._name) {cout << "Person(const Person&)" << endl;}
protected:string _name;
};class Student : public Person {
public:// 子類拷貝構造:顯式調用父類拷貝構造Student(const Student& s): Person(s)       // 父類拷貝構造(s切片給Person), _stuid(s._stuid){cout << "Student(const Student&)" << endl;}
protected:int _stuid;
};void Test() {Student s1("jack", 1001);Student s2(s1);  // 拷貝構造// 輸出:Person(const Person&) → Student(const Student&)
}

4.3 派生類的賦值重載(operator=)

  • 規則:派生類賦值重載必須調用父類賦值重載,否則父類部分的數據不會被賦值(淺拷貝問題);

  • 注意:賦值重載不會自動調用父類的,必須顯式用父類名::operator=調用。

示例:

class Person {
public:Person& operator=(const Person& p) {if (this != &p) {  // 防止自賦值_name = p._name;}cout << "Person::operator=" << endl;return *this;}
protected:string _name;
};class Student : public Person {
public:Student& operator=(const Student& s) {if (this != &s) {Person::operator=(s);  // 顯式調用父類賦值重載_stuid = s._stuid;}cout << "Student::operator=" << endl;return *this;}
protected:int _stuid;
};

4.4 派生類的析構函數

  • 規則 1:派生類析構函數會在自己執行完后,自動調用父類析構函數,保證 “先清理子類,再清理父類”(和構造順序相反);

  • 規則 2:析構函數名會被編譯器統一處理成destructor(),所以父類和子類的析構函數構成隱藏(不加virtual的情況下,后面多態會講virtual的作用)。

示例:

class Person {
public:~Person() {cout << "~Person()" << endl;}
};class Student : public Person {
public:~Student() {cout << "~Student()" << endl;}
};void Test() {Student s;// 析構順序:~Student() → ~Person()(自動調用父類析構)
}

五、繼承的 “特殊情況”:友元和靜態成員

5.1 友元不能繼承

父類的友元函數 / 類,不能訪問子類的私有 / 保護成員—— 友元關系是 “單向的”,只針對父類,不傳遞給子類。

示例:

class Student;  // 前置聲明class Person {// 父類友元:Display可以訪問Person的私有/保護成員friend void Display(const Person& p, const Student& s);
protected:string _name = "peter";
};class Student : public Person {
protected:int _stuid = 1001;  // 子類保護成員
};void Display(const Person& p, const Student& s) {cout << p._name << endl;  // 可以:訪問父類保護成員// cout << s._stuid << endl;  // 報錯:友元不能繼承,無法訪問子類保護成員
}

5.2 靜態成員在繼承體系中 “唯一”

父類定義的靜態成員(static),整個繼承體系中只有一個實例—— 不管派生出多少子類,所有類和對象共用這一個靜態成員(相當于 “全局變量”,但屬于類)。

示例:統計繼承體系中對象的總數:

class Person {
public:Person() { ++_count; }  // 構造時計數+1
public:static int _count;  // 靜態成員:統計對象總數
protected:string _name;
};// 靜態成員必須在類外初始化
int Person::_count = 0;class Student : public Person {};
class Graduate : public Student {};  // 子類的子類void Test() {Student s1, s2;Graduate g1;// 所有對象共用_count,總數=3cout << "對象總數:" << Person::_count << endl;  // 輸出3Student::_count = 0;  // 子類也能修改,因為共用cout << "對象總數:" << Person::_count << endl;  // 輸出0
}

六、繼承的 “老大難”:菱形繼承與虛擬繼承

這是 C++ 繼承的 “痛點”,也是面試高頻考點 —— 菱形繼承是多繼承的特殊情況,會導致數據冗余二義性,而虛擬繼承是解決這一問題的方案。

6.1 先理清:單繼承、多繼承、菱形繼承

  • 單繼承:子類只有一個直接父類(如Student → Person);

  • 多繼承:子類有兩個及以上直接父類(如Assistant → Student + Teacher);

  • 菱形繼承:多繼承的特殊情況 —— 兩個子類繼承同一個父類,又有一個子類繼承這兩個子類(形成 “菱形” 結構)。

結構示意圖:

        Person(頂層父類)/      \
Student        Teacher(中間子類,都繼承Person)\      /Assistant(底層子類,繼承Student和Teacher)

6.2 菱形繼承的 “坑”:數據冗余 + 二義性

看代碼示例,Assistant對象會有兩份Person的成員_name),導致兩個問題:

  1. 二義性:直接訪問_name時,不知道是Student繼承的還是Teacher繼承的;

  2. 數據冗余:兩份_name占用額外內存,且邏輯上應該只有一份(一個助教也是一個人,只需要一個姓名)。

示例:

class Person {
public:string _name = "peter";  // 頂層父類成員
};class Student : public Person { protected: int _stuid; };
class Teacher : public Person { protected: int _jobid; };// 菱形繼承:Assistant繼承Student和Teacher
class Assistant : public Student, public Teacher {
protected:string _major;
};void Test() {Assistant a;// a._name = "jack";  // 報錯:二義性(Student::_name還是Teacher::_name?)// 顯式指定可以解決二義性,但無法解決數據冗余(仍有兩份_name)a.Student::_name = "jack";a.Teacher::_name = "tom";cout << a.Student::_name << " " << a.Teacher::_name << endl;  // 輸出jack tom
}

6.3 解決方案:虛擬繼承(virtual)

中間子類StudentTeacher)繼承Person時,加上virtual關鍵字,即可解決菱形繼承的問題。

(1)使用方式

只需修改中間子類的繼承方式:

class Person {
public:string _name = "peter";
};// 中間子類:用virtual繼承Person
class Student : virtual public Person { protected: int _stuid; };
class Teacher : virtual public Person { protected: int _jobid; };// 底層子類正常繼承
class Assistant : public Student, public Teacher { protected: string _major; };void Test() {Assistant a;a._name = "jack";  // 正常:無歧義,_name只有一份cout << a._name << endl;  // 輸出jack
}
(2)虛擬繼承的 “原理”(通俗版)

虛擬繼承的核心是:讓中間子類(StudentTeacher)不再直接存儲父類(Person)的成員,而是通過 **“虛基表指針”** 指向 **“虛基表”**,虛基表中存儲了 “父類成員相對于當前類的偏移量”,通過偏移量找到唯一的父類成員。

簡單理解:

  • 中間子類(Student)多了一個 “虛基表指針”(指向虛基表);

  • 虛基表中存著 “到Person成員的距離”;

  • 底層子類(Assistant)通過兩個中間子類的虛基表指針,找到同一份Person成員,避免冗余和二義性。

不用深入底層內存細節,記住 “虛擬繼承讓頂層父類成員在底層子類中唯一” 即可。

(3)注意事項

只在菱形繼承的中間子類中使用虛擬繼承,其他場景不要用 —— 虛擬繼承會增加內存開銷(虛基表指針)和計算開銷(偏移量查找),沒必要。

七、繼承的 “終極思考”:繼承 vs 組合,該怎么選?

很多開發者濫用繼承,導致代碼耦合度高、難以維護。實際上,C++ 社區有個共識:優先使用組合,而非繼承

7.1 繼承:is-a 關系(是一種)

繼承體現的是 “is-a”(是一種)的邏輯 —— 比如BMW是一種CarStudent是一種Person

  • 優點:直接復用父類代碼,支持多態(后面講);

  • 缺點:耦合度高(子類依賴父類實現),破壞父類封裝(子類能訪問父類protected成員,父類修改會影響所有子類),屬于 “白箱復用”(子類知道父類內部細節)。

7.2 組合:has-a 關系(有一個)

組合體現的是 “has-a”(有一個)的邏輯 —— 比如Car有一個Tire(輪胎),Phone有一個Battery(電池)。

  • 優點:耦合度低(只需依賴被組合類的接口,不用知道內部細節),封裝性好,屬于 “黑箱復用”(被組合類的修改不影響組合類);

  • 缺點:需要手動調用被組合類的接口,代碼量略多。

7.3 選擇原則

  1. 用繼承的場景
  • 存在明確的 “is-a” 關系(如BMW → Car);

  • 需要實現多態(必須用繼承 +virtual)。

  1. 用組合的場景
  • 存在 “has-a” 關系(如Car → Tire);

  • 沒有明確的 “is-a” 關系,只是想復用代碼;

  • 追求低耦合、高維護性的場景(大多數業務場景)。

示例對比:

// 繼承:BMW is a Car
class Car { /* ... */ };
class BMW : public Car { /* ... */ };// 組合:Car has a Tire
class Tire { /* ... */ };
class Car {
protected:Tire _tire;  // 組合:Car有一個Tire
};

八、筆試面試高頻題(附答案)

  1. 什么是菱形繼承?菱形繼承的問題是什么?

    答:菱形繼承是多繼承的特殊情況:兩個子類繼承同一個頂層父類,又有一個底層子類繼承這兩個子類(形成菱形結構)。問題是數據冗余(底層子類有兩份頂層父類成員)和二義性(訪問頂層父類成員時無法確定來源)。

  2. 什么是菱形虛擬繼承?如何解決數據冗余和二義性?

    答:在菱形繼承的中間子類(繼承頂層父類的子類)中,用virtual關鍵字進行繼承,即為菱形虛擬繼承。解決原理是:中間子類通過 “虛基表指針” 指向 “虛基表”,虛基表存儲頂層父類成員的偏移量,讓底層子類只保留一份頂層父類成員,從而解決數據冗余和二義性。

  3. 繼承和組合的區別?什么時候用繼承?什么時候用組合?

    答:區別在于關系和耦合度:

  • 繼承是 “is-a” 關系,耦合度高(子類依賴父類實現,破壞封裝),白箱復用;

  • 組合是 “has-a” 關系,耦合度低(依賴接口,不依賴實現),黑箱復用。

    使用場景:

  • 繼承:is-a 關系、需要多態時;

  • 組合:has-a 關系、追求低耦合時(優先選擇)。

九、總結

繼承是 C++ 面向對象的核心,但也是一把 “雙刃劍”:用得好能大幅復用代碼,用得不好會導致耦合高、bug 多。這篇文章從基礎到復雜,幫你理清了繼承的核心規則、避坑點和最佳實踐,關鍵記住三點:

  1. 優先用public繼承,避免同名成員導致的隱藏;

  2. 遠離菱形繼承,萬不得已時用虛擬繼承;

  3. 優先選擇組合而非繼承,降低代碼耦合度。

建議你動手寫代碼測試本文的示例,比如菱形繼承的問題、虛擬繼承的效果、構造析構的調用順序,只有實踐才能真正掌握~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/923471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/923471.shtml
英文地址,請注明出處:http://en.pswp.cn/news/923471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Matplotlib定制:精解顏色、字體、線型與標記

Matplotlib定制&#xff1a;精解顏色、字體、線型與標記導語 Matplotlib 是 Python 數據可視化領域的基石。雖然它的默認樣式足以滿足快速分析的需求&#xff0c;但要創作出具有專業水準、信息清晰、視覺美觀的圖表&#xff0c;就必須掌握其強大的定制功能。本文將深入探討 Mat…

Qt開發經驗 --- Qt監聽文件/文件夾改變(17)

文章目錄[toc]1 概述2 演示效果3 簡單使用示例4 帶界面的使用示例5 源代碼地址更多精彩內容&#x1f449;內容導航 &#x1f448;&#x1f449;Qt開發經驗 &#x1f448;1 概述 QT實現實時監控文件的創建、修改、刪除操作 跟蹤文件夾內容的增刪改變化 可用于文件發生變化時自…

數據分析:合并一

&#x1f537; DA37&#xff1a;統計運動會項目報名人數&#xff08;僅輸出有人報名的項目&#xff09;? 題目描述給定兩個 CSV 文件&#xff1a;items.csv&#xff1a;包含項目信息&#xff08;item_id, item_name, location&#xff09;signup.csv&#xff1a;包含員工報名信…

WWW‘25一通讀 |圖Anomaly/OOD檢測相關文章(1)

寫在前面&#xff1a;進入新一輪學習階段&#xff0c;從閱讀開始。 本文分享的是WWW2025收錄的與作者研究相近的graph-based xx相關paper的閱讀筆記&#xff0c;含個人理解&#xff0c;僅供參考&#x1f604; 0x01 HEI&#xff1a;利用不變性原理實現異配圖結構分布偏移學習 J…

static_cast:C++類型系統的“正經翻譯官”

1. 背景與核心概念 1.1 C的“類型安全”哲學 想象一下&#xff0c;你所在的世界突然失去了所有規則&#xff1a;文字可以隨意變成數字&#xff0c;人可以瞬間變成椅子&#xff0c;汽車能飛上天變成飛機… 這聽起來像是瘋狂的夢境&#xff0c;但對于早期C語言來說&#xff0c;這…

【嵌入式原理系列-第八篇】USART從原理到配置全解析

目錄 一.通信領域基礎知識介紹 1.1 串行和并行通信 1.2 同步和異步傳輸 1.3 串口和COM口 1.4 通信協議標準以及物理層定義 1.5 物理層協議之TTL / RS-232 / RS-485 二.USART介紹 2.1 USART特點介紹 2.2 UART和TTL / RS-232 / RS-485 2.3 USART硬線流控介紹 2.4 USAR…

MariaDB介紹和MariaDB包安裝

文章目錄MariaDB介紹和安裝1.MariaDB介紹1.1 起源與背景1.2 核心特性1.2.1 高度兼容 MySQL1.2.2 優化的存儲引擎1.2.3 企業級功能增強1.2.4 性能優化1.2.5 安全增強1.3 社區與生態1.4 應用場景1.5 總結2.MariaDB安裝2.1 主機初始化2.1.1 設置網卡名2.1.2 設置ip地址2.1.3 配置鏡…

雙指針與滑動窗口算法精講:從原理到高頻面試題實戰

引言&#xff1a;算法選擇的十字路口 在算法面試中&#xff0c;雙指針和滑動窗口如同兩把瑞士軍刀&#xff0c;能高效解決80%以上的數組和字符串問題。本文將深入解析這兩種技術的核心差異&#xff0c;結合力扣高頻題目&#xff0c;提供可直接復用的代碼。 一、算法核心思想解析…

蘋果MAC、MacBook air和pro安裝windows雙系統與iOS分發

文章目錄1. main1.1 準備工作1.2 啟動轉換助理1.3 Windows安裝1.4 蘋果電腦安裝Windows雙系統切換2. 蘋果(iOS)分發/上架2.1 上架App Store2.2 上架TestFlight2.3 webClip免簽上架2.4 超級簽名2.5 企業證書2.6 app分發系統Reference1. main 蘋果電腦安裝windows雙系統 https:…

ArcGIS定向影像(1)——非傳統影像輕量級解決方案

常常聽到這樣的需求&#xff0c;ArcGIS能讓用戶自己低成本的做出谷歌街景嗎&#xff1f;現在 _ArcGIS Pro 3.2 和 ArcGIS Enterprise 11.2 _能夠讓用戶不使用任何插件和擴展的情況下完成街景數據集的構建&#xff0c;數據管理&#xff0c;發布服務和調用的完整解決方案。非常體…

uni-app 網絡之封裝實戰HTTP請求框架

前言在uniapp開發中&#xff0c;網絡請求是每個應用都必不可少的功能模塊。一個優秀的網絡請求封裝不僅能提高開發效率&#xff0c;還能增強代碼的可維護性和可擴展性。本文將基于實際項目經驗&#xff0c;詳細介紹如何封裝一個高效、可維護的Uniapp網絡請求框架&#xff0c;并…

架構師成長之路-架構方法論

文章目錄前言一、先搞懂&#xff1a;架構師不僅僅是“技術大佬”&#xff0c;更是“問題解決者”1.1 架構師的分類&#xff1a;不止“開發架構師”一種1.2 架構師要關注什么&#xff1f;別只盯著技術1.3 架構師解決問題的4步心法&#xff1a;從定義到落地1.4 架構師的成長攻略&…

uniapp在微信小程序中實現 SSE 流式響應

前言 最近需要使用uniapp開發一個智能對話頁面&#xff0c;其中就需要使用SSE進行通信。 本文介紹下在uniapp中如何基于uni.request實現SSE流式處理。 在線體驗 #小程序:yinuosnowball SSE傳輸格式 返回輸出的流式塊: Content-Type為text/event-stream 每個流式塊均為 d…

STM32N6AI資料匯總

文章目錄前言一、STM32N6硬件資源1.1 NUCLEO-N657X0-Q1.2 STM32N6570-DK1.3 正點原子STM32N647二、STM32N6軟件資源2.1 STM32CubeN6例程資源包2.2 STM32圖像信號處理器&#xff08;ISP&#xff09;調優軟件2.3 正點原子N6開發板配套軟件三、AI軟件資源3.1 STM32N6 AI軟件包總結…

Flask學習筆記(一)

1、環境準備pip install Flask使用Flask開發第1個入門程序&#xff1a;from flask import Flask app Flask(__name__) app.route(/) def hello_world():return Hello, World!if __name__ __main__:app.run()Flask構造函數將當前模塊的名稱(__name__)作為參數。2、route函數ap…

CSP認證練習題目推薦(4)

思維、貪心、綜合 排隊打水 這道題目不算難&#xff0c;但是不注意還是會出現很多錯誤&#xff0c;比如結構體的書寫。以及自定義結構體排序。還有這里做的優化&#xff0c;使用前綴和記錄打水的等待時間&#xff0c;但是這里很容易出錯的點在于等待時間是應該是記錄的前一個…

MySQL 視圖的更新與刪除:從操作規范到風險防控

MySQL 視圖的更新與刪除&#xff1a;從操作規范到風險防控 視圖作為 “虛擬表”&#xff0c;其更新與刪除操作常常讓開發者困惑 ——“為什么更新視圖會報錯&#xff1f;”“刪除視圖會不會弄丟數據&#xff1f;” 實際上&#xff0c;80% 的視圖操作問題都源于對 “視圖依賴基表…

C 語言實現 I.MX6ULL 點燈(續上一篇)、SDK、deep及bsp工程管理

目錄 一、匯編點燈轉 C 語言實現 1. 關鍵字&#xff1a;volatile 2. 寄存器地址定義&#xff08;兩種方式&#xff09; &#xff08;1&#xff09;直接宏定義地址 &#xff08;2&#xff09;結構體封裝寄存器&#xff08;優化訪問&#xff09; 3. 核心功能代碼 &#xff…

DevOps實戰(7) - 使用Arbess+GitPuk+sourcefare實現Node.js項目自動化部署

Arbess 是一款國產開源免費的 CI/CD 工具&#xff0c;工具支持一鍵部署&#xff0c;頁面簡潔易用。本文將詳細介紹如何安裝配置使用GitPuk、sourcefare、Arbess系統&#xff0c;使用流水線拉取GitPuk源碼、使用sourcefare代碼掃描、構建安裝包并進行主機部署。 1、GitPuk 安裝…

算法,蒜鳥蒜鳥-P1-理解“雙指針”

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 有很多很多不足的地方&#xff0c;歡迎評論交流&#xff0c;感謝您的閱讀和評論&#x1f604;。 目錄引言1 雙指針&#xff1a;Two Pointers1.1 左右指…