【C++高階(一)】繼承

目錄

一、繼承的概念

1.繼承的基本概念

2.繼承的定義和語法

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

?編輯?4.總結

二、基類和派生類對象賦值轉換

三、繼承中的作用域

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

1.派生類中的默認構造函數

2.派生類中的拷貝構造函數

3.派生類中的移動構造函數

4.派生類的拷貝賦值運算符

?5.派生類的移動賦值運算符

6.派生類的析構函數

為什么基類析構函數需要virtual關鍵字修飾?

理由:多態性和正確的析構順序

問題:非虛析構函數導致的資源泄漏

總結

五、繼承和友元

六、繼承與靜態成員

?七、復雜的菱形繼承和菱形虛擬繼承

1.單繼承

2.多繼承

3.菱形繼承

?菱形繼承的問題

4.菱形虛擬繼承

5.虛擬繼承解決數據冗余和二義性的原理

虛基表的工作機制


一、繼承的概念

在C++中,繼承是一種面向對象編程的重要特性,它允許一個類(稱為派生類或子類)從另一個類(稱為基類或父類)繼承屬性和行為(成員變量和成員函數)。通過繼承,派生類不僅可以擁有基類的所有成員,還可以擴展或修改這些成員以提供更具體或特殊的功能。

1.繼承的基本概念

  1. 基類(Base Class):提供基礎屬性和行為的類。
  2. 派生類(Derived Class):從基類繼承并擴展或修改其功能的類。
  3. 訪問控制(Access Control)
    • Public 繼承:基類的public和protected成員在派生類中保持其訪問級別不變,public成員依然是public,protected成員依然是protected。
    • Protected 繼承:基類的public和protected成員在派生類中都變為protected成員。
    • Private 繼承:基類的public和protected成員在派生類中都變為private成員。
  4. 構造函數和析構函數:派生類的構造函數在執行前會先調用基類的構造函數,析構函數的調用順序則相反,先調用派生類的析構函數,再調用基類的析構函數。
  5. 多重繼承(Multiple Inheritance):C++允許一個派生類從多個基類繼承。

2.繼承的定義和語法

class Base {
public:int baseValue;void baseFunction() {// 基類成員函數}
};class Derived : public Base {
public:int derivedValue;void derivedFunction() {// 派生類成員函數}
};

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

?4.總結

  1. 基類private成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管是在類內還是在類外都不能去訪問它。
  2. 基類private成員在派生類中是不能被訪問的,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為protected。可以看出保護成員限定符是繼承才出現的。
  3. 實際上面的表格我們進行一下總結就能發現,基類的私有成員在子類中都是不可見的。基類的其他成員在子類的訪問方式 == Min(成員在基類的訪問限定符,繼承方式),public > protected > private。
  4. 使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式。
  5. 在實際運用中一般都是使用public繼承,幾乎很少使用protected/private繼承,也不提倡使用protected/private繼承,因為?protected/private繼承下來的成員都只能在派生類的類里使用,實際中擴展維護性不強。

二、基類和派生類對象賦值轉換

  • 派生類對象 可以賦值給 基類的對象 / 基類的指針 / 基類的引用。這里有個形象的說法叫切片或者切割。寓意把派生類中父類那部分切來賦值過去。
  • 基類對象不能賦值給派生類對象。
  • 基類的指針或者引用可以通過強制類型轉換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。

class Base {
public:int baseValue;virtual void display() {std::cout << "Base class" << std::endl;}
};class Derived : public Base {
public:int derivedValue;void display() override {std::cout << "Derived class" << std::endl;}
};Base baseObj;
Derived derivedObj;baseObj = derivedObj;  // 對象切割發生
baseObj.display();     // 輸出 "Base class"

在上面的例子中,盡管derivedObj賦值給了baseObj,但baseObj只保留了Base類的部分,派生類的derivedValue被切割掉了,調用display函數時也只會調用基類的版本

此外,C++允許使用基類的指針或引用來指向派生類對象,這可以實現多態性。多態性允許你通過基類接口調用派生類的重載函數。

Base* basePtr = &derivedObj;
basePtr->display();  // 輸出 "Derived class"(多態性)Base& baseRef = derivedObj;
baseRef.display();  // 輸出 "Derived class"(多態性)

?在上面代碼中,basePtr和baseRef都指向Derived對象,并且調用display方法時,會調用派生類Derived中的版本,這是因為display函數被聲明為virtual。(virtual關鍵字我們下面會講)

另外還有類型轉換:static_cast和dynamic_cast,感興趣的可以去了解下。

三、繼承中的作用域

1. 在繼承體系中 基類 派生類 都有 獨立的作用域
2. 子類和父類中有同名成員, 子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫隱藏,
也叫重定義。 (在子類成員函數中,可以 使用 基類 :: 基類成員 顯示訪問
3. 需要注意的是如果是成員函數的隱藏,只需要函數名相同就構成隱藏。
4. 注意在實際中在 繼承體系里 面最好 不要定義同名的成員
// Student的_num和Person的_num構成隱藏關系,可以看出這樣代碼雖然能跑,但是非常容易混淆
class Person
{
protected :string _name = "小李子"; // 姓名int _num = 111; ? // 身份證號
};
class Student : public Person
{
public:void Print(){cout<<" 姓名:"<<_name<< endl;cout<<" 身份證號:"<<Person::_num<< endl;cout<<" 學號:"<<_num<<endl;}
protected:int _num = 999; // 學號
};
void Test()
{Student s1;s1.Print();
};
// B中的fun和A中的fun不是構成重載,因為不是在同一作用域
// B中的fun和A中的fun構成隱藏,成員函數滿足函數名相同就構成隱藏。
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);
};

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

在之前的學習中, 我們知道類可以自動生成一些默認的成員函數,這些成員函數包括默認構造函數、拷貝構造函數、移動構造函數、拷貝賦值運算符、移動賦值運算符和析構函數。而對于派生類,這些默認成員函數的生成和行為有一些特殊的規則和注意事項,下面我講詳細介紹。

1.派生類中的默認構造函數

默認構造函數在沒有用戶定義的構造函數時自動生成。對于派生類的默認構造函數,它會調用基類的默認構造函數(如果存在),然后初始化派生類的成員。而如果基類沒有默認構造函數,則必須在派生類構造函數的初始化列表階段顯示調用。

class Base {
public:Base() {std::cout << "Base default constructor" << std::endl;}
};class Derived : public Base {
public:Derived() {std::cout << "Derived default constructor" << std::endl;}
};int main() {Derived d;  // 輸出:Base default constructor//      Derived default constructorreturn 0;
}

2.派生類中的拷貝構造函數

拷貝構造函數在沒有用戶定義的情況下自動生成,用于創建類的對象副本。派生類的拷貝構造函數會首先調用基類的拷貝構造函數,然后復制派生類的成員。

class Base {
public:Base(const Base&) {std::cout << "Base copy constructor" << std::endl;}
};class Derived : public Base {
public:Derived(const Derived& other) : Base(other) {std::cout << "Derived copy constructor" << std::endl;}
};int main() {Derived d1;Derived d2 = d1;  // 輸出:Base copy constructor//      Derived copy constructorreturn 0;
}

3.派生類中的移動構造函數

移動構造函數在沒有用戶定義的情況下自動生成,用于移動資源所有權。派生類的移動構造函數會首先調用基類的移動構造函數,然后移動派生類的成員。

class Base {
public:Base(Base&&) noexcept {std::cout << "Base move constructor" << std::endl;}
};class Derived : public Base {
public:Derived(Derived&& other) noexcept : Base(std::move(other)) {std::cout << "Derived move constructor" << std::endl;}
};int main() {Derived d1;Derived d2 = std::move(d1);  // 輸出:Base move constructor//      Derived move constructorreturn 0;
}

4.派生類的拷貝賦值運算符

拷貝賦值運算符在沒有用戶定義的情況下自動生成,用于將一個對象的內容賦值給另一個對象。派生類的拷貝賦值運算符會首先調用基類的拷貝賦值運算符,然后賦值派生類的成員。

class Base {
public:Base& operator=(const Base&) {std::cout << "Base copy assignment operator" << std::endl;return *this;}
};class Derived : public Base {
public:Derived& operator=(const Derived& other) {Base::operator=(other);std::cout << "Derived copy assignment operator" << std::endl;return *this;}
};int main() {Derived d1, d2;d1 = d2;  // 輸出:Base copy assignment operator//      Derived copy assignment operatorreturn 0;
}

?5.派生類的移動賦值運算符

移動賦值運算符在沒有用戶定義的情況下自動生成,用于將一個對象的內容移動到另一個對象。派生類的移動賦值運算符會首先調用基類的移動賦值運算符,然后移動派生類的成員。

class Base {
public:Base& operator=(Base&&) noexcept {std::cout << "Base move assignment operator" << std::endl;return *this;}
};class Derived : public Base {
public:Derived& operator=(Derived&& other) noexcept {Base::operator=(std::move(other));std::cout << "Derived move assignment operator" << std::endl;return *this;}
};int main() {Derived d1, d2;d1 = std::move(d2);  // 輸出:Base move assignment operator//      Derived move assignment operatorreturn 0;
}

6.派生類的析構函數

析構函數在沒有用戶定義的情況下自動生成,用于清理對象派生類的析構函數會首先調用派生類的析構函數,然后調用基類的析構函數。

class Base {
public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};int main() {Base* b = new Derived();delete b;  // 輸出:Derived destructor//      Base destructorreturn 0;
}

為什么基類析構函數需要virtual關鍵字修飾?

基類的析構函數需要加virtual關鍵字是為了確保在刪除派生類對象時能夠正確調用析構函數。這是一個非常重要的概念,尤其是在使用多態性和通過基類指針或引用操作派生類對象時。

理由:多態性和正確的析構順序

class Base {
public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};

當基類的析構函數是虛函數時,通過基類指針刪除派生類對象時,C++會首先調用派生類的析構函數,然后再調用基類的析構函數。這確保了派生類中分配的資源可以先被正確釋放,再釋放基類中分配的資源。

int main() {Base* b = new Derived();delete b;  // 輸出順序:Derived destructor//           Base destructorreturn 0;
}

問題:非虛析構函數導致的資源泄漏

如果基類的析構函數不是虛函數,則通過基類指針刪除派生類對象時,只會調用基類的析構函數,而不會調用派生類的析構函數。這會導致派生類中的資源沒有被正確釋放,造成資源泄漏。

class Base {
public:~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};int main() {Base* b = new Derived();delete b;  // 只輸出:Base destructorreturn 0;
}

在上述代碼中,由于 Base 類的析構函數不是虛函數,刪除 b 時只調用了 Base 的析構函數,Derived 類的析構函數沒有被調用,這會導致 Derived 類中的資源沒有被正確釋放。

總結

為了確保在使用多態性時派生類對象可以被正確地銷毀,避免資源泄漏,基類的析構函數應該聲明為虛函數。這一做法可以保證刪除派生類對象時,派生類和基類的析構函數都能被正確調用。以下是總結的要點:

  1. 多態性支持:使用基類指針或引用操作派生類對象時,確保正確調用派生類的析構函數。
  2. 正確的析構順序:先調用派生類的析構函數,再調用基類的析構函數,確保資源正確釋放。
  3. 避免資源泄漏:防止派生類中的資源沒有被釋放,導致內存泄漏或其他資源泄漏。

五、繼承和友元

友元關系是單向的和局部的,友元關系不能繼承!也就是說基類友元不能訪問子類私有和保護成員。

#include <iostream>// 基類
class Base {
private:int basePrivateVar;
protected:int baseProtectedVar;
public:int basePublicVar;Base() : basePrivateVar(1), baseProtectedVar(2), basePublicVar(3) {}friend void baseFriendFunction(Base &obj);
};// 基類的友元函數
void baseFriendFunction(Base &obj) {std::cout << "Base Private Var: " << obj.basePrivateVar << std::endl;std::cout << "Base Protected Var: " << obj.baseProtectedVar << std::endl;
}// 派生類
class Derived : public Base {
private:int derivedPrivateVar;
protected:int derivedProtectedVar;
public:int derivedPublicVar;Derived() : derivedPrivateVar(4), derivedProtectedVar(5), derivedPublicVar(6) {}friend void derivedFriendFunction(Derived &obj);
};// 派生類的友元函數
void derivedFriendFunction(Derived &obj) {// 基類的友元不能訪問派生類的私有或保護成員// std::cout << "Derived Private Var: " << obj.derivedPrivateVar << std::endl; // 錯誤// std::cout << "Derived Protected Var: " << obj.derivedProtectedVar << std::endl; // 錯誤std::cout << "Derived Public Var: " << obj.derivedPublicVar << std::endl;
}int main() {Base baseObj;Derived derivedObj;baseFriendFunction(baseObj); // 可以訪問Base類的私有和保護成員derivedFriendFunction(derivedObj); // 可以訪問Derived類的公共成員// 基類的友元函數不能訪問派生類的私有和保護成員// baseFriendFunction(derivedObj); // 錯誤return 0;
}

六、繼承與靜態成員

基類定義了static成員,則整個繼承體系里面只有一個這樣的成員。無論派生出多少個子類,都只有一個static成員實例。

靜態成員變量需要在類外進行定義和初始化。靜態成員函數則不需要在類外定義。

#include <iostream>// 基類
class Base {
public:static int staticVar;  // 聲明靜態成員變量static void staticFunction() {  // 聲明并定義靜態成員函數std::cout << "Static Function in Base" << std::endl;}
};// 定義靜態成員變量
int Base::staticVar = 10;// 派生類
class Derived : public Base {
public:void display() {std::cout << "Base staticVar: " << staticVar << std::endl;  // 訪問基類的靜態成員變量staticFunction();  // 調用基類的靜態成員函數}
};int main() {Derived obj;obj.display();// 靜態成員可以通過類名直接訪問Base::staticVar = 20;Derived::staticVar = 30;std::cout << "Base staticVar after modification: " << Base::staticVar << std::endl;std::cout << "Derived staticVar after modification: " << Derived::staticVar << std::endl;return 0;
}

靜態成員的特點

  1. 類共享性:所有類的對象共享同一個靜態成員變量。
  2. 類作用域:靜態成員變量和靜態成員函數在類作用域內,但可以通過類名直接訪問。
  3. 內存管理:靜態成員變量在程序啟動時分配內存,程序結束時釋放內存。

注意:

  • 靜態成員函數:靜態成員函數不能訪問非靜態成員變量和非靜態成員函數,因為它們屬于類本身,而不是類的某個對象。但是靜態成員函數可以訪問靜態成員變量和其他靜態成員函數。

?七、復雜的菱形繼承和菱形虛擬繼承

1.單繼承

一個子類只有一個直接父類時稱這個繼承關系為單繼承。

2.多繼承

一個子類有兩個或以上直接父類時稱這個繼承關系為多繼承

3.菱形繼承

菱形繼承(也稱鉆石繼承)是指一種特殊的多繼承情況,其中一個類從兩個基類繼承,而這兩個基類又繼承自同一個祖先類。這種繼承關系形成了一個菱形結構。

?菱形繼承的問題

?從上面的對象成員模型構造,可以看出菱形繼承有數據冗余和二義性的問題。在Assistant的對象中Person成員會有兩份。

#include <iostream>// 祖先類
class A {
public:int value;A() : value(0) {}
};// 兩個派生類繼承自 A
class B : public A {};
class C : public A {};// 派生類 D 同時繼承自 B 和 C
class D : public B, public C {};int main() {D obj;// obj.value; // 錯誤:二義性問題,不知道是從 B 繼承的 A 還是從 C 繼承的 A// 解決方法之一是明確指定路徑obj.B::value = 1;obj.C::value = 2;std::cout << "obj.B::value: " << obj.B::value << std::endl;std::cout << "obj.C::value: " << obj.C::value << std::endl;return 0;
}

4.菱形虛擬繼承

為了解決上面菱形繼承所帶來的問題,我們可以使用虛擬繼承。虛擬繼承確保在菱形繼承結構中只存在一個基類的實例。

如在上面的代碼中,我們可以在B和C繼承A的時候使用虛擬繼承,即

class B : virtual public A {};
class C : virtual public A {};

需要注意的是,虛擬繼承不要在其他地方去使用。

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;
};
int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

下面是菱形繼承的內存對象成員模型:這里可以看到數據冗余

下面是菱形虛擬繼承的內存對象成員模型:

?

這里可以分析出D對象中將A放到了D對象組成的最下面,這個A同時屬于B和C,那么B和C如何去找到公共的A呢?

這里是通過了B和C的兩個指針,指向的一張表。這兩個指針叫虛基表指針,這兩個表叫虛基表。虛基表中存儲的是偏移量,通過偏移量就能找到A?。

總結:

虛擬繼承通過確保每個虛擬基類在派生類中只有一個共享實例,從而避免了重復實例化和二義性問題。為了實現這一點,編譯器會使用虛基表來跟蹤和管理虛擬基類的實例。

虛基表的工作機制

  1. 虛基表的引入: 每個使用虛擬繼承的類會包含一個虛基表指針。這個指針指向一個虛基表,該表包含虛擬基類的指針。

  2. 共享基類實例: 在派生類(如 D)的對象中,虛基表指針確保所有虛擬基類實例都指向同一個實際基類實例。這意味著 D 中只有一個 A 類的實例。

  3. 成員訪問的重定向: 在訪問基類成員時,編譯器使用虛基表來正確地定位基類成員,確保訪問的是唯一的基類實例。

所以,當一個類虛擬繼承另一個類時,編譯器在對象布局中插入一個虛基表指針(vbptr)。這個指針指向一個虛基表(vbtbl),而虛基表中包含指向虛擬基類的偏移量或地址。通過這種方式,每個派生類能夠正確地定位并訪問唯一的虛擬基類實例。


上面就是我們對C++繼承的全部理解了~

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

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

相關文章

英語學習筆記25——Mrs. Smith‘s kitchen

Mrs. Smith’s kitchen 史密斯太太的廚房 詞匯 Vocabulary Mrs. 夫人【已婚】 復習&#xff1a;Mr. 先生 全名 / 姓    Mrs. 夫人 全名 / 丈夫的姓    Miss 小姐&#xff08;未婚&#xff09; 全名 / 姓    Ms. 女士 全名 / 姓 查看婚姻狀況&#xff0c;可以觀察…

springboot項目中圖片上傳之后需要重啟工程才能看到圖片?

需求背景 最近在做一個用戶自定義上傳頭像的小需求&#xff0c;用戶上傳頭像然后需要立馬回顯。 需求是很常見的、正當的需求。如果不使用到對象存儲這類服務&#xff0c;我們把用戶頭像的圖片文件僅存在本地就可以了。我們在開發的過程中為了工程管理方便通常下意識會將圖片…

freertos串口DMA隊列發送卡死

調試回調函數的時候&#xff0c;我在cube中刪除了默認的DMA通道&#xff0c;又新增了另外一個通道&#xff0c;導致NVIC中&#xff0c;該通道的優先級為0&#xff0c;后來改成了5就正常了。

Modbus TCP轉Profinet網關測試配置案例

本案例采用XD-ETHPN20網關做為Modbus TCP通信協議設備與Profinet通信協議設備連接的橋梁。Modbus TCP是一種基于TCP/IP協議的工業通信協議&#xff0c;而Profinet則是用于太網通信的協議。Modbus TCP轉Profinet網關可實現這兩種不同協議之間的數據交換和傳輸&#xff0c;極大地…

算法刷題筆記 逆序對的數量(C++實現)

文章目錄 題目描述解題代碼&#xff08;蠻力版&#xff09;解題代碼&#xff08;基于歸并排序&#xff09; 題目描述 給定一個長度為n的整數數列&#xff0c;請你計算數列中的逆序對的數量。逆序對的定義如下&#xff1a;對于數列的第i個和第j個元素&#xff0c;如果滿足i<…

Python高級進階--dict字典

dict字典?? 1. 字典簡介 dictionary&#xff08;字典&#xff09; 是 除列表以外 Python 之中 最靈活 的數據類型&#xff0c;類型為dict 字典同樣可以用來存儲多個數據字典使用鍵值對存儲數據 2. 字典的定義 字典用{}定義鍵值對之間使用,分隔鍵和值之間使用:分隔 d {中…

【ECharts】數據可視化

目錄 ECharts介紹ECharts 特點Vue2使用EChats步驟安裝 ECharts引入 ECharts創建圖表容器初始化圖表更新圖表 示例基本柱狀圖后臺代碼vue2代碼配置 組件代碼運行效果 基本折線圖示例代碼組件 基礎餅圖示例代碼后臺前端配置組件運行效果 其他 ECharts介紹 ECharts 是一個由百度開…

spring模塊(一)容器(4)ApplicationContextAware

一、介紹 1、問題引入 為了獲取已被實例化的Bean對象,如果使用再次加載配置文件的方法,可能會出現一個問題,如一些線程配置任務, 會啟動兩份,產生了冗余. ApplicationContext appContext new ClassPathXmlApplicationContext("applicationContext.xml"); UserS…

python 多線程處理圖片

thread for i in range(len(ori_path)):for filename in os.listdir(ori_path[i]):number_img number_img 1print("正在處理第" str(number_img) "張圖片")img_name ori_path[i] filenamet Thread(target deal_one_img, args [img_name, filenam…

使用.net core 調用C#WebService的三種方式

WebSerrvic代碼&#xff1a; [WebMethod]public string Test(string p1, string p2){return p1 "_" p2;} 以下是 SOAP 1.2 請求和響應示例。所顯示的占位符需替換為實際值。 POST /Service1.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charsetutf-8…

unity 制作app實現底部導航欄和頂部狀態欄

前段時間在用unity制作一個app&#xff0c;發現有個問題用unity制作的app&#xff0c;他默認是沒有頂部狀態欄的&#xff0c;也沒有底部的導航欄&#xff0c;是一個全部覆蓋的狀態。但仔細觀察可以發現&#xff0c;正常app&#xff0c;頂部狀態欄是有的&#xff0c;而且是透明的…

軟件設計師備考 | 案例專題之數據庫設計 概念與例題

相關概念 關注上圖中的兩個部分&#xff1a; 概念結構設計 設計E-R圖&#xff0c;也即實體-聯系圖。 工作步驟&#xff1a;選擇局部應用、逐一設計分E-R圖、E-R圖合并。進行合并時&#xff0c;它們之間存在的沖突主要有以下3類&#xff1a; 屬性沖突。同一屬性可能會存在于…

低功耗藍牙模塊輕松實現智能防丟器

低功耗藍牙模塊&#xff0c;作為集成藍牙無線技術功能的PCBA板&#xff0c;主要用于短距離無線通訊&#xff0c;已經成為物聯網無線傳輸發展的中堅力量。隨著藍牙技術不斷更新換代&#xff0c;越來越多的智能可穿戴設備出現在我們的生活中&#xff0c;智能手環&#xff0c;智能…

電商公司需不需要建數字檔案室呢

建立數字檔案室對于電商公司來說是非常有必要的。以下是一些原因&#xff1a; 1. 空間節約&#xff1a;數字檔案室可以將紙質文件轉化為電子文件&#xff0c;節省了大量存儲空間。這對于電商公司來說尤為重要&#xff0c;因為他們通常會有大量的訂單、客戶信息和供應商合同等文…

Java面向對象程序設計-Hash表

以下為翁愷老師在3.4Hash表中的示例代碼&#xff1a; package coins;import java.util.HashMap; import java.util.Scanner;public class Coin {private HashMap<Integer,String> coinnamesnew HashMap<Integer,String>();public Coin(){coinnames.put(1,"pe…

貸款業務——還款方式以及計算公式對比

文章目錄 等額本息等額本金先息后本&#xff08;按月付息&#xff0c;到期還本&#xff09;到期一次還本付息等本等息&#xff08;等額等息&#xff09;等本等息&#xff08;砍頭息&#xff09; 等額本息 等額本息&#xff1a;借款人每月還的金額固定&#xff08;本金利息總額…

力扣538. 把二叉搜索樹轉換為累加樹

Problem: 538. 把二叉搜索樹轉換為累加樹 文章目錄 題目描述思路復雜度Code 題目描述 思路 利用二叉搜索樹中序遍歷的特性&#xff0c;**降序遍歷&#xff08;此處是想表達先遍歷其右子樹再遍歷其左子樹這樣遍歷的過程中每個節點值得大小排序是降序得&#xff09;**其節點&…

寶塔PHP環境安裝配置Xdebug

寶塔PHP環境安裝配置Xdebug 安裝XdebugVSCode安裝插件編輯配置文件編輯配置運行調試斷點快捷鍵其他 安裝Xdebug 在寶塔中&#xff0c;找到PHP&#xff0c;打開管理頁面&#xff0c;選擇xdebug擴展&#xff0c;點擊操作欄中的安裝按鈕&#xff08;這里已經安裝過了&#xff0c;…

砍死怪獸的概率

題目描述&#xff1a;給定3個參數&#xff0c;N&#xff0c;M&#xff0c;K&#xff0c;怪獸有N滴血&#xff0c;等著英雄來砍自己&#xff0c;英雄每一次打擊&#xff0c;都會讓怪獸流失[0,M]的血量&#xff0c;流失的值每次在[0,M]上等概率的獲得一個值&#xff0c;求K次打擊…

kafka單機安裝及性能測試

kafka單機安裝及性能測試 Apache Kafka是一個分布式流處理平臺&#xff0c;最初由LinkedIn開發&#xff0c;并于2011年開源&#xff0c;隨后成為Apache項目。Kafka的核心概念包括發布-訂閱消息系統、持久化日志和流處理平臺。它主要用于構建實時數據管道和流處理應用&#xff0…