【C++詳解】深入解析繼承 類模板繼承、賦值兼容轉換、派生類默認成員函數、多繼承與菱形繼承

文章目錄

  • 一、繼承概念
  • 二、繼承定義
    • 定義格式
    • 繼承后基類成員訪問方式的變化
    • 類模板的繼承
  • 三、基類和派?類間的轉換(賦值兼容轉換)
  • 四、繼承中的作用域
    • 隱藏規則
    • 兩道筆試常考題
  • 五、派生類的默認成員函數
    • 四個常見默認成員函數
    • 實現?個不能被繼承的類
  • 六、繼承與友元
  • 七、繼承與靜態成員
  • 八、多繼承及其菱形繼承問題
    • 繼承模型
    • 虛繼承
  • 九、繼承和組合


一、繼承概念

繼承(inheritance)機制是?向對象程序設計使代碼可以復?的最重要的?段,它允許我們在保持原有類特性的基礎上進?擴展,增加?法(成員函數)和屬性(成員變量),這樣產?新的類,稱派?類。繼承呈現了?向對象程序設計的層次結構,體現了由簡單到復雜的認知過程。以前我們接觸的函數層次的復?,繼承是類設計層次的復?
下面小編來舉個例子,如果要創建兩個類teacher和student,我們就可以把類公有的信息或者函數比如姓名/地址/電話/年齡等成員變量,都有identity?份認證的成員函數放在父類里,創建teacher和student時直接繼承父類里的內容,把類獨有的定義在各自的子類里,這樣就可以簡化代碼,避免重復定義。

//父類
class Person
{
public:// 進?校園/圖書館/實驗室刷?維碼等?份認證void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "張三"; // 姓名string _address; // 地址string _tel; // 電話int _age = 18; // 年齡
};//子類
class Student : public Person
{
public:// 學習void study(){// ...}
protected:int _stuid; // 學號
};//子類
class Teacher : public Person
{
public:// 授課void teaching(){//...}
protected:string title; // 職稱
};

二、繼承定義

定義格式

下?我們看到Person是基類,也稱作?類。Student是派?類,也稱作?類。

在這里插入圖片描述

繼承方式有三種,和我們之前介紹的訪問限定符同名。

在這里插入圖片描述

繼承后基類成員訪問方式的變化

我們前面介紹了繼承方式和訪問限定符,那么父類的訪問限定符里的內容繼承到子類后在子類中的訪問方式是怎樣的呢?我們首先要清楚一共有9中排列組合的方式,因父類中的一種訪問方式有三種繼承方式,詳情見下圖:

在這里插入圖片描述

回顧:類中的public成員是類里類外都可以訪問,protect和private成員是類外不可訪問,類里可以訪問。

1、我們先看表格中最特別的最后一行,在基類的private成員不論什么繼承方式在子類中都不可見,不可見是比private更高一級的限制,它是指不可見的內容在派生類的類里類外都無法訪問,當然這里的無法訪問不是絕對的,可以通過派生類里的共有或者保護成員函數間接訪問,在實踐中我們是很少把基類成員定義為私有的。
2、剩下的六種成員在派生類里的訪問方式是將成員在基類的訪問限定符和繼承方式相比較取小,大小關系如下:public > protected > private
3、基類private成員在派?類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派?類中能訪問,就定義為protected。可以看出保護成員限定符是因繼承才出現的。
4、繼承方式也可以像訪問限定符一樣不顯示寫,使?關鍵字class定義派生類時默認的繼承?式是private,使?struct時默認的繼承?式是public,不過最好顯?的寫出繼承?式。
5、在實際運?中?般使?都是public繼承,?乎很少使?protetced/private繼承,也不提倡使?protetced/private繼承,因為protetced/private繼承下來的成員都只能在派?類的類??使?,實際中擴展維護性不強。

類模板的繼承

在此之前小編先科普一下復用,復用有兩種方式,一種的組合,也就是我們熟悉的容器適配器模式,類里面直接包含,我直接包含你比如stack類直接包含deque,有些大佬稱之為has-a,還有一種就是繼承,一個類繼承于另一個類,我是一個特殊的你,這是is-a。

小編回到主題來講類模板的繼承,我們前面實現的普通類型父類繼承到子類后子類是可以直接調用父類的函數的,但如果是類模板的話,子類是無法直接調用父類的成員函數的,因為父類是模板沒有實例化成具體代碼,所以編譯器無法確定父類成員的合法性,需要顯示聲明函數的來源才能調用,比如下面通過訪問限定符:

namespace bit
{//template<class T>//class vector//{};// stack和vector的關系,既符合is-a,也符合has-atemplate<class T>class stack : public vector<T>{public:void push(const T& x){// 基類是類模板時,需要指定?下類域,// 否則編譯報錯:error C3861: “push_back”: 找不到標識符// 因為stack<int>實例化時,也實例化vector<int>了// 但是模版是按需實例化,push_back等成員函數未實例化,所以找不到//push_back(x);vector<T>::push_back(x);}void pop(){vector<T>::pop_back();}const T& top(){return vector<T>::back();}bool empty(){return vector<T>::empty();}};

三、基類和派?類間的轉換(賦值兼容轉換)

首先我們要明確兩個普通類是無法像下面三種方式一樣轉換的。
1、通常情況下我們把一個類型的對象賦值給另一個類型的指針或者引用時,不一般會發生構造+拷貝構造,存在類型轉換,中間會產生臨時對象,所以需要加 const,如:int a = 1; const double& d = a;。 在C++中,完全獨立的類型是不支持隱式類型轉換的,除非兩個類有繼承關系。public 繼承中,就是一個特殊處理的例外,派生類對象可以賦值給基類的指針 / 基類的引用,而不需要加 const,(意味著沒有產生臨時對象)這里的指針和引用綁定是派生類對象中的基類部分,如下圖所示。也就意味著一個基類的指針或者引用,可能指向基類對象,也可能指向派生類對象。

在這里插入圖片描述

2、除了給引用和指針,子類對象也可以直接賦值給父類對象。派生類對象賦值給基類對象是通過基類的拷貝構造函數或者賦值重載函數完成的
(這兩個函數的細節后面小節會細講),這個過程就像派生類自己定義部分成員切掉了一樣,所以也被叫做切割或者切片,如下圖中所示。

在這里插入圖片描述

3、基類對象不能賦值給派生類對象。

4、基類的指針或者引用可以通過強制類型轉換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。這里基類如果是多態類型,可以使用RTTI (Run-Time Type Information) 的 dynamic_cast 來進行識別后進行安全轉換。(我們后面在多態章節再細講)

四、繼承中的作用域

隱藏規則

  1. 在繼承體系中基類和派?類都有獨?的作?域,所以基類和派生類中可以定義同名成員,在派生類成員中訪問同名成員時默認會先訪問派生類的,派生類沒有才回去訪問基類的,如果只想訪問基類的同名成員可以指定作用域訪問。
  2. 派?類和基類中有同名成員,派?類成員將屏蔽基類對同名成員的直接訪問,這種情況叫隱藏成員變量隱藏的底層邏輯是作用域查找規則,因為訪問一個變量時會去找它的定義,查找順序是默認會先在派生類查找,派生類沒有才會去基類找。(在派?類成員函數中,可以使? 基類::基類成員 顯?訪問)
  3. 需要注意的是如果是成員函數的隱藏,只需要函數名相同就構成隱藏,成員函數隱藏的底層邏輯是函數隱藏規則,當派生類定義了一個與基類同名的函數(不管參數列表是否相同),基類中的同名函數就會被隱藏。我們以下面例題的代碼為例,一旦在派生類中定義了 void fun(int i),基類中的 void fun() 在派生類的作用域內就不再可見,并且也編譯器也不會主動再到基類里查找是否有匹配的函數。(除非使用作用域解析運算符 :: 顯式指定調用基類版本 )。
  4. 注意在實際中在繼承體系??最好不要定義同名的成員。

兩道筆試常考題

1、A和B類中的兩個func構成什么關系()
A. 重載 B. 隱藏 C.沒關系
2、下?程序的編譯運?結果是什么()
A. 編譯報錯 B.運?報錯 C. 正常運?

class A
{
public:void func(){cout << "func()" << endl;}
};class B : public A
{
public:void func(int i){cout << "func(int i)" << i << endl;}
};int main()
{B b;b.func(10);b.func();return 0;
};

首先要明確函數重載要求在同一作用域,這里顯然在兩個作用域,又因為兩個函數同名,符合隱藏規則的第三點,所以第一題選B。
第二題我們根據隱藏規則的第三點,當調用 b.fun() 時,因為函數隱藏規則,編譯器只會在派生類 B 的作用域內查找匹配的函數。由于在 B類中只定義了 void fun(int i) ,沒有 void fun() ,所以編譯器找不到匹配的函數,就會報錯,選A。

五、派生類的默認成員函數

基類的默認成員函數符合我們在類和對象章節介紹的規則,這里我們來探討派生類的默認成員函數。學習默認成員函數要從以下幾個方面入手:我們不寫編譯器會不會自動生成、怎么生成?默認生成的符不符合我們預期?不符合預期話我們自己寫要如何寫?

四個常見默認成員函數

我們首先要認識到默認成員函數是用來處理成員變量的,派生類的默認成員函數的行為和普通類的默認成員函數高度相類似,只是派生類的成員變量有兩部分,派生類的默認成員函數會把兩個部分的成員變量分開處理,一個是繼承自父類的基類成員,另一個是派生類自己定義的派生類成員,其中派生類成員的處理方式和普通類一樣,并且是由派生類自己的的默認成員函數處理。基類成員會被打包看成一個整體,然后調用基類的默認成員函數,統一由基類的默認成員函數處理。可以簡單理解成派生類相比普通類多了一個自定義類型成員。

小記:const成員、引用成員,沒有默認構造的自定義類型成員都必須顯示在構造函數的初始化列表初始化。(默認構造對其他成員可以隨便給一個值,反正在類里還可以賦值修改,const成員和引用成員無法做到)
const成員、引用成員在類的作用域里只有一次給值也就是初始化的機會,引用成員規定不能先定義再初始化,因為引用只能引用一個成員無法變更去引用其他成員,const成員一旦定義后就無法更改了所以它倆都必須在定義時就初始化,在類里變量都要最先走初始化列表初始化,所以const成員、引用成員只能顯示在初始化列表初始化。
在初始化列表階段編譯器會自動調用自定義類型成員的默認構造函數,如果這個自定義類型成員沒有默認構造函數,編譯器就無法完成自動初始化,構造函數體內部的代碼是在所有成員都完成初始化之后才執行的,如果成員的類沒有默認構造函數,編譯器在進入函數體之前就會因無法初始化該成員而報錯,(語法規定允許內置類型在進入構造函數體之前不初始化,自定義類型成員必須在進入構造函數體之前初始化)所以沒有默認構造的自定義類型成員也必須在初始化列表顯示初始化。

接下來我們以下面這段代碼為例依次介紹派生類的其中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 _address;
};int main()
{Student s1;return 0;
}
  • 構造函數

派?類的構造函數必須調?基類的默認構造函數初始化基類的那?部分成員。如果基類沒有默認的構造函數,就相當于我們上面小記里的第三種特殊情況,則必須在派?類構造函數的初始化列表階段顯?調?。
上面這段代碼因為基類有默認構造函數初始化派生類里的基類成員,代碼不會報錯,如果基類沒有默認構造那么我們就需要自己顯示寫構造函數來初始化基類成員了,基類成員必須調用基類的構造函數初始化,并且調用只能走派生類構造函數的初始化列表,因為系統默認先初始化父類的成員(通過父類的構造函數),再初始化子類的成員,最后執行子類構造函數體的代碼,(如果有成員未初始化是不會進入構造函數體內部的)而且只有這樣可以避免先初始化子類成員導致訪問未初始化的父類資源,引發未定義行為。這也和我們下面介紹的析構函數順序對稱。

class Student : public Person
{
public:Student(const char* name, int num, const char* address)// 顯示調用基類構造函數初始化基類成員 :Person(name)    //:_name(name) 錯誤寫法,_num(num),_address(address){ }protected:int _num; //學號string _address;
};

(自己想的補充:兩個獨立的類之間,不能像子類調用父類構造函數那樣
“跨類初始化成員”,但可以在一個類的初始化列表中調用另一個類的構造函數,來初始化自身包含的該類類型成員。)

  • 拷貝構造

派?類的拷?構造函數必須調?基類的拷?構造完成基類的拷?初始化。一般拷貝構造都不需熬我們自己寫,除非需要深拷貝。構造函數一般都要自己寫,因為需要傳參去構造。
派?類的拷?構造函數拷貝父類成員就需要我們在派生類中傳一個父類對象的引用去調用父類的拷?構造函數,派生類調用基類拷貝構造過程中不會產生臨時對象,首先賦值兼容轉換不會產生,然后調用基類拷貝構造時因為是同類型直接構造也不會產生臨時對象。
下面的例子其實不用手動寫編譯器默認生成的就夠用了,這里小編只是演示一下如果需要自己手動寫要如何寫。

Student(const Student& s): Person(s) //調用父類拷貝構造(賦值兼容轉換支持), _num(s._num), _address(s._address)
{//編譯器自動生成的就夠用了//存在深拷貝才自己寫
}
  • 賦值運算符重載
  • 派?類的operator=必須要調?基類的operator=完成基類的復制。需要注意的是派?類和基類的賦值運算符重載是同名函數,所以派?類的operator=隱藏了基類的operator=,所以顯?調?基類的operator=,需要指定基類作?域。
//因為要判斷避免自己給自己賦值所以不走初始化列表
Student& operator=(const Student& s)
{if (this != &s){Person::operator=(s);  //同名函數必須指定作用域_num = s._num;_address = s._address;}return *this;
}
  • 析構函數

這里要注意一點派生類的析構函數規則和前面三個成員函數不同,派生類的析構函數只用顯示釋放派生類自己創建的資源,如果有資源的話。因為派生類的析構執行完畢后會自動調用基類的析構,如果在派生類的析構函數中再顯示釋放基類資源的話就會釋放兩次。
析構資源的順序和構造正好相反,先析構子類,再析構父類。如果先析構父類那么就有可能因為在子類中訪問父類被析構的成員而報錯,若先析構子類的話父類是訪問不到子類的成員的。
所以這里我們也明白了為什么基類析構函數是自動調用。系統為了保證先析構子類,再析構父類這個順序,所以在執行完派生類構造函數后會去自動調用基類的構造函數,如果在派生類構造函數中顯示析構父類和派生類的話這個順序就無法保證。

實現?個不能被繼承的類

?法1:基類的構造函數私有,派?類的基類成員必須調?基類的構造函數初始化,但是基類的構造函數私有化以后,派?類看不?無論是顯示還是編譯器自動都無法調用到基類的構造函數了,那么派?類就?法實例化出對象。(語法規定實例化對象必須經過兩個步驟:分配內存和初始化成員)這樣將基類的構造函數私有后基類也無法實例化出對象了。
?法2:C++11新增了?個final關鍵字,final修改基類,派?類就不能繼承了。

// C++11的?法
class Base final
{
public:void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
private:// C++98的?法//Base()//{}
};class Derive :public Base
{void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};int main()
{Base b;Derive d;return 0;
}

六、繼承與友元

友元關系不能繼承,也就是說基類友元不能訪問派?類私有和保護成員。解決方法就是讓基類友元也成為派?類的友元。

//前置聲明
class Student;class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{//friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 學號
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;  //無法訪問
}
int main()
{Person p;Student s;// 編譯報錯:error C2248: “Student::_stuNum”: ?法訪問 protected 成員// 解決?案:Display也變成Student 的友元即可Display(p, s);return 0;
}

七、繼承與靜態成員

靜態成員會被繼承。基類定義了static靜態成員,則整個繼承體系??只有?個這樣的成員。?論派?出多少個派?類,都只有?個static成員實例。

class Person
{
public:string _name;static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stuNum;
};
int main()
{Person p;Student s;// 這?的運?結果可以看到?靜態成員_name的地址是不?樣的// 說明派?類繼承下來了,?派?類對象各有?份cout << &p._name << endl;cout << &s._name << endl;// 這?的運?結果可以看到靜態成員_count的地址是?樣的// 說明派?類和基類共?同?份靜態成員cout << &p._count << endl;cout << &s._count << endl;// 公有的情況下,?派?類指定類域都可以訪問靜態成員cout << Person::_count << endl;cout << Student::_count << endl;return 0;
}

八、多繼承及其菱形繼承問題

繼承模型

單繼承:?個派?類只有?個直接基類時稱這個繼承關系為單繼承
多繼承:?個派?類有兩個或以上直接基類時稱這個繼承關系為多繼承,多繼承對象在內存中的模型是,先繼承的基類在前?,后?繼承的基類在后?,派?類成員在放到最后?。

在這里插入圖片描述

菱形繼承:菱形繼承是多繼承的?種特殊情況。菱形繼承的問題,從下?的對象成員模型構造,可以看出菱形繼承有數據冗余和?義性的問題,在Assistant的對象中Person成員會有兩份。?持多繼承就?定會有菱形繼承,像Java就直接不?持多繼承,規避掉了這?的問題,所以實踐中我們也是不建議設計出菱形繼承這樣的模型的。
數據冗余是指同一個基類的數據在派生類中存在多份拷貝,浪費內存空間,也可能導致數據不一致。
?義性小編用下面的例子解釋,當Assistant對象訪問基類Person對象成員_name時編譯器無法確定應該訪問來自Student的_name還是Teacher的。

在這里插入圖片描述

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; // 主修課程
};int main()
{// 編譯報錯:error C2385: 對“_name”的訪問不明確Assistant a;//a._name = "peter";// 需要顯?指定訪問哪個基類的成員可以解決?義性問題,但是數據冗余問題?法解決a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

虛繼承

虛繼承是用來解決菱形繼承數據冗余和?義性的。
很多?說C++語法復雜,其實多繼承就是?個體現。有了多繼承,就存在菱形繼承,有了菱形繼承就有菱形虛擬繼承,底層實現就很復雜,性能也會有?些損失,所以最好不要設計出菱形繼承。多繼承可以認為是C++的缺陷之?,后來的?些編程語?都沒有多繼承,如Java。
虛繼承格式是在繼承方式符前面加virtual,例子如下。虛繼承應在父類派生多個子類時加,多個父類派生一個子類是正常的多繼承,不用加。
虛繼承的底層原理小編大致說一下,就相當于把person 從student和teacher里拿出來,放在Assistant對象整體的開頭或者結尾,具體看編譯器。

class Person
{
public:string _name; // 姓名/*int _tel;* int _age;string _gender;string _address;*/// ...
};// 使?虛繼承Person類
class Student : virtual public Person
{
protected:int _num; //學號
};// 使?虛繼承Person類
class Teacher : virtual public Person
{
protected:int _id; // 職?編號
};// 教授助理
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修課程
};int main()
{// 使?虛繼承,可以解決數據冗余和?義性Assistant a;a._name = "peter";return 0;
}

九、繼承和組合

繼承和組合都是復用思想的體現。
1、public繼承是?種is-a的關系。也就是說每個派?類對象都是?個基類對象。
2、組合是?種has-a的關系。假設B組合了A,每個B對象中都有?個A對象。例如容器適配器,stack里有一個deque。
3、繼承允許你根據基類的實現來定義派?類的實現。(因為一般都是共有繼承,子類能訪問父類成員)這種通過?成派?類的復?通常被稱為?箱復?(white-box reuse)。術語“?箱”是相對可視性??:在繼承?式中,基類的內部細節對派?類可?
。繼承?定程度破壞了基類的封裝,基類的改變,對派?類有很?的影響。派?類和基類間的依賴關系很強,耦合度?
4、對象組合是類繼承之外的另?種復?選擇。新的更復雜的功能可以通過組裝或組合對來獲得。對象組合要求被組合的對象具有良好定義的接?。這種復??格被稱為?箱復(black-box reuse),因為對象的內部細節是不可?的。對象只以“?箱”的形式出現。
組類之間沒有很強的依賴關系,耦合度低。優先使?對象組合有助于你保持每個類被封裝。
5、優先使?組合,?不是繼承。實際盡量多去?組合,組合的耦合度低,代碼維護性好。不過也不太那么絕對,類之間的關系適合繼承(is-a)那就?繼承,另外要實現多態,也必須要繼承。類之間的關系既適合?繼承(is-a)也適合組合(has-a),就?組合。

以上就是小編分享的全部內容了,如果覺得不錯還請留下免費的關注和收藏如果有建議歡迎通過評論區或私信留言,感謝您的大力支持。
一鍵三連好運連連哦~~

在這里插入圖片描述

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

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

相關文章

加法器 以及ALU(邏輯算術單元)

加法器框架&#xff0c;首先介紹原理&#xff0c;然后引入一位加法器最后再引入多位加法器最后引入帶符號的加法器這一節涉及到的硬件電路的知識理解就好&#xff0c;實在看不懂就跳過&#xff0c;但是封裝以后的功能必須看懂。這是一個一般的加法過程涉及到的必要元素圖中已經…

設計模式實戰:自定義SpringIOC(親手實踐)

上一篇&#xff1a;設計模式實戰&#xff1a;自定義SpringIOC&#xff08;理論分析&#xff09; 自定義SpringIOC&#xff08;親手實踐&#xff09; 上一篇文章&#xff0c;我們介紹了SpringIOC容器的核心組件及其作用&#xff0c;下面我們來動手仿寫一個SpringIOC容器&#…

力扣面試150(42/150)

7.28 20. 有效的括號 給定一個只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判斷字符串是否有效。 有效字符串需滿足&#xff1a; 左括號必須用相同類型的右括號閉合。左括號必須以正確的順序閉合。每個右括號都有一…

基于黑馬教程——微服務架構解析(二):雪崩防護+分布式事務

之前的兩篇文章我們介紹了微服務的基礎概念及其服務間通信機制。本篇將深入探討微服務的核心保障&#xff1a;服務保護與分布式事務。一、微服務保護問題描述&#xff1a; 在一個購物車的微服務中&#xff0c;倘若某一項服務&#xff08;服務A&#xff09;同一時刻訪問的數據十…

LeetCode: 429 N叉樹的層序遍歷

題目描述給定一個 N 叉樹&#xff0c;返回其節點值的層序遍歷&#xff08;即從左到右&#xff0c;逐層訪問每一層的所有節點&#xff09;。示例輸入格式&#xff08;層序序列化&#xff09;&#xff1a;輸入示意&#xff1a;1/ | \3 2 4/ \5 6輸出&#xff1a;[[1], [3,2,4…

使用phpstudy極簡快速安裝mysql

使用 phpStudy 極簡快速安裝 MySQL 的完整指南&#xff1a; 一、phpStudy 簡介 phpStudy 是一款 Windows 平臺下的 PHP 環境集成包&#xff0c;包含&#xff1a; Apache/Nginx PHP 5.x-7.x MySQL 5.5-8.0 phpMyAdmin 二、安裝步驟 1. 下載安裝包 訪問官網下載&#xf…

git lfs使用

apt install git lfs 或者下載二進制文件加到環境變量 https://github.com/git-lfs/git-lfs/releases git lfs install git lfs clone huggingface文件路徑 如果訪問不了hugggingface.co用hf-mirror.com替代&#xff0c;國內下載速度還是挺快的 先按照pip install modelscope m…

6、CentOS 9 安裝 Docker

&#x1f433; CentOS 9 安裝 Docker 最全圖文教程&#xff08;含鏡像源優化與常見問題解決&#xff09;標簽&#xff1a;CentOS 9、Docker、容器技術、開發環境、國內鏡像源 適合讀者&#xff1a;后端開發、運維工程師、Linux 初學者&#x1f4cc; 前言 在 CentOS 9 上安裝 Do…

SystemV消息隊列揭秘:原理與實戰

目錄 一、消息隊列的基本原理 1、基本概念 2、基本原理 3、消息類型的關鍵作用 4、重要特性總結 5、生命周期管理 6、典型應用場景 二、System V 消息隊列的內核數據結構 1、消息隊列的管理結構 msqid_ds&#xff08;消息隊列標識符結構&#xff09; 關鍵字段解析 2…

5 分鐘上手 Firecrawl

文章目錄Firecrawl 是什么&#xff1f;本地部署驗證mcp安裝palyground&#x1f525; 5 分鐘上手 FirecrawlFirecrawl 是什么&#xff1f; 一句話&#xff1a; 開源版的 “最強網頁爬蟲 清洗引擎” ? 自動把任意網頁 → 結構化 Markdown / JSON ? 支持遞歸整站抓取、JS 渲染…

算法訓練營day31 貪心算法⑤56. 合并區間、738.單調遞增的數字 、968.監控二叉樹

貪心算法的最后一篇博客&#xff01;前面兩道題都是比較簡單的思路&#xff0c;重點理解一下最后一道題即可。有一說一&#xff0c;進入到貪心算法這一章節之后&#xff0c;我的博客里和代碼注釋里的內容明顯少了很多&#xff0c;因為很多貪心的題目我覺得不需要很復雜的文字說…

Jenkins流水線部署+webhook2.0

文章目錄1. 環境2. 用到的插件3. 流水線部署腳本1. 環境 Centos7Jenkins2.5.0JDKopen17阿里云倉庫 注意&#xff1a;這個版本兼容需要特別注意&#xff0c;要不然會很麻煩 2. 用到的插件 Generic Webhook Trigger 3. 流水線部署腳本 兼容鉤子部署&#xff08;webhook&…

IDM下載失敗排查

網絡連接問題排查檢查網絡連接是否穩定&#xff0c;確保能夠正常訪問互聯網 測試其他下載工具或瀏覽器是否能夠正常下載 嘗試關閉防火墻或殺毒軟件&#xff0c;排除安全軟件攔截的可能性代理和VPN設置檢查確認IDM的代理設置是否正確&#xff0c;是否與系統代理一致 檢查是否使用…

Anaconda安裝時的幾個操作

一、安裝Anaconda 其實Anaconda的安裝比較簡單&#xff0c;點擊next就好了。在安裝中需要注意以下兩點&#xff1a; 1、選擇安裝路徑 在安裝時&#xff0c;路徑最好選擇非C盤&#xff0c;且路徑中不要出現中文&#xff0c;以免后期運行代碼時出現不必要的錯誤。 我安裝時&…

網易易盾、騰訊ACE等主流10款游戲反外掛系統對比

本文將深入對比10款游戲反外掛系統&#xff1a;1.網易易盾&#xff1b;2.Ricochet Anti?Cheat&#xff1b;3.BattlEye&#xff1b;4.幾維安全手游智能反外掛系統&#xff1b;5.伏魔AI反外掛&#xff1b;6.Riot Vanguard&#xff1b;7.Xigncode3&#xff1b;8.盛大GPK&#xff…

wpa_supplicant-2.10交叉編譯

參考文章:https://blog.csdn.net/weixin_45783574/article/details/145810790 1、Openssl交叉編譯 1.1 下載openssl-1.1.1t.tar.gz 下載網址: https://openssl-library.org/source/old/1.1.1/index.html1.2 編譯 sudo tar xvf openssl-1.1.1t.tar.gz cd openssl-1.1

源碼解讀SpringCloudAlibaba Nacos2.x

Nacos 服務注冊 Nacos 服務注冊時&#xff0c;客戶端會將自己的信息注冊到Nicosserver上&#xff0c;形成key-value組合&#xff0c;其中key通常是服務名稱&#xff0c;value是實例地址信息。在二點X版本中&#xff0c;客戶端通過Spring Boot的擴展機制(例如web_initialized事件…

Windows 11 下 Anaconda 命令修復指南及常見問題解決

Windows 11 下 Anaconda 命令修復指南及常見問題解決 在使用 Anaconda 過程中&#xff0c;可能會遇到環境損壞、更新失敗、包依賴沖突等問題。本文整理了一套通過命令行修復 Anaconda 的完整方案&#xff0c;適用于 Windows 11 系統&#xff0c;同時補充了權威參考鏈接供深入學…

安寶特案例丨全球連線!安寶特Vuzix與RodsCones共筑實時手術教育平臺

安寶特Vuzix與合作伙伴Rods&Cones協作&#xff0c;為Rocamed在布拉格UROSANIT診所舉辦的創新型實時手術直播研討會提供技術賦能。 本次直播通過合作伙伴Rods&Cones軟件平臺搭載安寶特Vuzix智能眼鏡&#xff0c;成功連接來自9國、3大洲、6個時區的27位醫生&#xff0c;…

【Spring Boot 快速開發】一、入門

目錄Spring Boot 簡介Web 入門Spring Boot 快速入門HTTP 協議概述請求協議響應協議解析協議TomcatSpring Boot 簡介 Spring Boot 是由 Pivotal 團隊&#xff08;后被 VMware 收購&#xff09;開發的基于 Spring 框架的開源項目&#xff0c;于 2014 年首次發布。其核心目標是簡…