C++進階——多態

? ? ? ? ?

づ?ど

?🎉?歡迎點贊支持🎉

個人主頁:勵志不掉頭發的內向程序員;

專欄主頁:C++語言;


文章目錄

前言

一、多態的概念

二、多態的定義及實現

2.1、多態的構成條件

(1)虛函數

(2)虛函數的重寫/覆蓋

?(3)實現多態的兩個必須重要條件

(4)虛函數重寫的一些其他問題

(5)override 和 final 關鍵字

(6)重載/重寫/隱藏的對比

三、純虛函數和抽象類

四、多態的原理

4.1、虛函數表指針

4.2、多態的原理

(1)多態是如何實現的

(2)動態綁定與靜態綁定

(3)虛函數表

總結


前言

學習了繼承以后,我們接著來看看我們繼承的延伸—多態,多態的內容也是非常的多,我們大家得仔細學習,我們想要成為?C++ 大佬是無論如何都繞不開我們繼承和多態的。


一、多態的概念

多態通俗來說就是多種形態。多態分為編譯時多態(靜態多態)和運行時多態(動態多態),這里我們重點講運行時多態。

編譯時多態主要就是我們前面講的函數重載和函數模板,他們通過傳不同類型的參數就可以調用不同的函數,通過參數的不同達到多種形態,之所以叫編譯時多態,是因為他們實參傳給形參的參數匹配是在編譯時完成的,我們把編譯時一般歸為靜態,運行時歸為動態。

運行時多態,具體點就是去完成某個行為(函數),可以傳不同的對象就會完成不同的行為,就達到多種形態。比如吃飯這個行為,當中國人吃飯時,拿的是筷子調羹;美國人吃飯時,拿的是刀和叉子;印度人吃飯時直接用手等。再比如同樣是動物叫的一個行為(函數),傳貓對象過去就是 "喵喵";狗對象過去就是 "汪汪"。

二、多態的定義及實現

2.1、多態的構成條件

(1)虛函數

類成員函數前面加 virtual 修飾,那么這個成員函數就被稱為虛函數。注意非成員函數不能加 virtual 修飾。

class Person
{
public:// 虛函數virtual void EatFood(){cout << "用餐具吃飯" << endl;}
};

(2)虛函數的重寫/覆蓋

虛函數的重寫/覆蓋:派生類中有一個跟基類完全相同的虛函數,即派生類虛函數與基類虛函數的返回值、函數名字、參數列表完全相同(簡稱?"三同"),稱派生類的虛函數重寫了基類的虛函數。

class Person
{
public:virtual void EatFood(){cout << "用餐具吃飯" << endl;}
};class Chinese : public Person
{
public:// 此處構成虛函數的覆蓋/重寫virtual void EatFood(){cout << "用筷子吃飯" << endl;}
};

我們發現上面的代碼在重寫我們的虛函數時,派生類的虛函數前也加了 virtual 關鍵字。但其實派生類的虛函數在不加 virtual 關鍵字時,也可以構成重寫(因為繼承后基類的虛函數被繼承下來了在派生類依舊保持虛函數屬性),但是這樣寫不是非常規范,不建議這樣使用。我們要明白虛函數的具體寫法,而不是看前面有沒有 virtual。

class Person
{
public:virtual void EatFood(){cout << "用餐具吃飯" << endl;}
};class Chinese : public Person
{
public:// 此處依然構成虛函數的覆蓋/重寫void EatFood(){cout << "用筷子吃飯" << endl;}
};

這樣寫也可以,但是強烈不建議。

?(3)實現多態的兩個必須重要條件

我們在學習完我們上面的內容,我們結合上面內容來看看實現多態的兩個重要條件。

  • 必須是基類的指針或者引用調用虛函數。
  • 被調用的函數必須是虛函數,并且完成了虛函數重寫/覆蓋。

我們在繼承那一章節學習了切片,子類和子類的指針引用可以賦值給我們父類以達到切片的效果,但是反過來就不太行,所以這里的函數就得調用基類的指針或者引用,這樣我們不管是傳父類還是子類(子類會被父類切片)都可以傳參。

同時子類必須對父類的虛函數進行重寫/覆蓋,不然我們不管調用父類的函數還是子類的函數都是一樣的效果,也沒辦法實現多態的不同形態的效果。

class Person
{
public:// 虛函數virtual void EatFood(){cout << "用餐具吃飯" << endl;}
};class Chinese : public Person
{
public:// 基類虛函數的重寫/覆蓋virtual void EatFood(){cout << "用筷子吃飯" << endl;}
};void Func(Person* ptr)
{// 這?可以看到雖然都是Person指針Ptr在調?EatFood// 但是跟ptr沒關系,?是由ptr指向的對象決定的。ptr->EatFood();
}int main()
{Person ps;Chinese ce;Func(&ps);Func(&ce);return 0;
}

看上去我們都是調用的同一個函數,但其實這個函數的輸出是和我們傳的 ptr 的類型是有關聯的,指向父類調用父類,指向子類調用子類。

我們傳給函數 Person 的 引用也是可以的。

void Func(Person& ptr)
{ptr.EatFood();
}

但是如果我們破環掉這兩個必須的重要條件,我們的程序就無法實現多態的效果了。

破壞第一條,我們傳值給 ptr。

void Func(Person ptr)
{ptr.EatFood();
}

或者破壞第二條,我們不去給我們的從父類繼承下來的虛函數進行重寫。

class Chinese : public Person
{
public:
};void Func(Person& ptr)
{ptr.EatFood();
}

我們的多態都會失效。

當然,我們多個子類之間也可以實現多態的條件。

class Animal
{
public:virtual void talk() const{}
};class Dog : public Animal
{
public:virtual void talk() const{cout << "汪汪" << endl;}
};class Cat : public Animal
{
public:virtual void talk() const{cout << "(>^ω^<)喵" << endl;}
};class Mouse : public Animal
{
public:virtual void talk() const{cout << "吱吱" << endl;}
};void letsHear(const Animal& animal)
{animal.talk();
}int main()
{Cat cat;Dog dog;Mouse mouse;letsHear(cat);letsHear(dog);letsHear(mouse);return 0;
}

(4)虛函數重寫的一些其他問題

協變(了解)

派生類重寫基類虛函數時,與基類函數返回值類型不同。即基類虛函數返回基類對象的指針或者引用,派生類虛函數返回派生類對象的指針或者引用,稱為協變。協變的實際意義并不大,所以我們了解一下即可。

class A{};class B : public A {};class Person
{
public:virtual A* EatFood(){cout << "用餐具吃飯" << endl;return nullptr;}
};class Chinese : public Person
{
public:virtual B* EatFood(){cout << "用筷子吃飯" << endl;return nullptr;}
};void Func(Person& ptr)
{// 這?可以看到雖然都是Person指針Ptr在調?EatFood// 但是跟ptr沒關系,?是由ptr指向的對象決定的。ptr.EatFood();
}int main()
{Person ps;Chinese ce;Func(ps);Func(ce);return 0;
}

我們的虛函數的返回值不相同,父類對象返回一個父類指針或引用,子類對象返回一個子類指針或引用。同樣可以實現多態的效果,我們就稱為協變。

析構函數的重寫

基類的析構函數為虛函數,此時派生類析構函數只要定義,無論是否加 virtual 關鍵字,都與基類的析構函數構成重寫。雖然基類與派生類析構函數名字不同看起來不符合重寫規則,實際上編譯器對析構函數的名稱做了特殊處理,編譯后析構函數的名稱統一處理成 destructor,所以基類的析構函數加了 virtual 修飾,派生類的析構函數就構成重寫了。

class A
{
public:virtual ~A(){cout << "~A()" << endl;}
};class B : public A 
{
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};

我們這里可以看到,我們 B 的析構名字和 A 的不一樣,按道理來說是不會構成重寫的,但實際上它們是構成的,因為我們編譯器會把析構的名字統一處理成 destructor,所以此時它們名字相同了就構成了重寫了。

我們要讓析構函數變成虛函數的原因是因為當我們創建了一個父類的指針,有可能指向父類對象,也有可能指向子類對象。如果指向子類對象,此時我們調用父類的 delete 就會無法析構子類的那一部分。

int main()
{// 沒問題A* p1 = new A;delete p1;// 有問題A* p2 = new B;delete p2;return 0;
}

我們 delete p2 時我們其實是期望它調用子類的析構函數的,不然就會出現問題。所以我們就得實現虛函數的重寫去讓我們指向父類調用父類,指向子類調用子類。

(5)override 和 final 關鍵字

從上面可以看出,C++ 對函數重寫的要求比較嚴格,但是有些情況下由于疏忽,比如函數名寫錯參數寫錯等導致無法構成重寫,而這種錯誤在編譯期間是不會報出的,而在程序運行時沒有得到預期結果去 debug 就會得不償失,因此 C++11 提供了 override,可以幫助用戶檢測是否重寫。如果我們不想讓派生類重寫這個虛函數,那么可以用 final 去修飾。

class Car 
{
public:virtual void Dirve(){}
};class Benz :public Car {
public:virtual void Drive() override { cout << "Benz-舒適" << endl;}
};

此時我們的 Dirve 由于寫錯了而導致無法構成重寫,此時我們在后面加入的 override 就起作用了。

當然,如果有基類的成員函數不想被重寫,加入 final 就不能被繼承了。

class Car
{
public:virtual void Drive() final {}
};class Benz :public Car
{
public:virtual void Drive() { cout << "Benz-舒適" << endl; }
};

(6)重載/重寫/隱藏的對比

它們都有函數之間的關系,相似性極高。

三、純虛函數和抽象類

在虛函數的后面寫上 =0,則這個函數為純虛函數,純虛函數不需要定義實現(實現沒啥意義,因為要被派生類重寫,但是語法上可以實現),只要聲明即可。包含純虛函數的類叫做抽象類,抽象類不能實例化出對象,如果派生類繼承后不重寫純虛函數,那么派生類也是抽象類。純虛函數某種程度上強制了派生類重寫虛函數,因為不重寫實例化不出對象。

class Car
{
public:virtual void Drive() = 0;
};class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒適" << endl;}
};class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};

此時我們 Car 中的 Drive 就是我們的純虛函數,Car 就是我們的抽象類。我們的抽象類無法實例出對象。

int main()
{// 編譯報錯?法實例化抽象類Car car;return 0;
}

正是因為無法實例化出對象,所以我們沒有必要去實現我們的純虛函數,因為無法調用。

如果我們的子類不去實現我們的純虛函數,我們的類依舊是抽象類,因為依舊會繼承我們的父類的純虛函數。但是我們的子類也無法實例化出對象了。

class Benz :public Car
{
public:
};int main()
{Car* pBenz = new Benz;pBenz->Drive();return 0;
}

所以說我們的純虛函數的作用就是強制我們的派生類去實現這個純虛函數,不然就無法調用。當我們實現了,就能夠成功調用了。

int main()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();return 0;
}

四、多態的原理

4.1、虛函數表指針

我們來算一下下面代碼的運行結果。

class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
protected:int _b = 1;char _ch = 'x';
};int main()
{Base b;cout << sizeof(b) << endl;return 0;
}

我們大家一看,這不是很簡單嗎,Base 類中有一個 int 整型,char 整型,所以就是 8 字節秒了。實則不然。

我們的答案是 12 字節,我們通過調試發現,除了 _b 和 _c 成員,還多一個 _vfptr 放在對象的前面(有的平臺可能放對象后面,和平臺有關)。

對象中的這個指針我們叫做虛函數表指針(v 表示 virtual,f 表示 function)。一個含有虛函數的類中都至少有一個虛函數表指針,因為一個類所有的虛函數的地址要被放到這個類對象的虛函數中,虛函數表也稱為虛表。嚴格來說,它是一個函數指針數組。

我們嘗試多加幾個虛函數來看看效果。
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Func2()" << endl;}virtual void Func3(){cout << "Func3()" << endl;}protected:int _b = 1;char _ch = 'x';
};

4.2、多態的原理

(1)多態是如何實現的

從底層的角度 Func 函數中的 ptr->BuyTicket(),是如何作為 ptr 指向 Person 對象調用 Person::BuyTicket,ptr 指向 Student 對象調用 Student::BuyTicket 的呢?

class Person 
{
public:virtual void BuyTicket() { cout << "買票-全價" << endl; }
private:string _name;
};class Student : public Person 
{
public:virtual void BuyTicket() { cout << "買票-打折" << endl; }
private:string _id;
};class Soldier : public Person 
{
public:virtual void BuyTicket() { cout << "買票-優先" << endl; }
private:string _codename;
};void Func(Person* ptr)
{// 這?可以看到雖然都是Person指針Ptr在調?BuyTicket// 但是跟ptr沒關系,?是由ptr指向的對象決定的。ptr->BuyTicket();
}int main()
{// 其次多態不僅僅發?在派?類對象之間,多個派?類繼承基類,重寫虛函數后// 多態也會發?在多個派?類之間。Person ps;Student st;Soldier sr;Func(&ps);Func(&st);Func(&sr);return 0;
}

通過下圖我們可以看到,滿足多態條件后,底層不再是編譯時通過調用對象確定函數的地址,而是運行時到指向的對象的虛表中確定對應的虛函數的地址,這樣就實現了指針或引用指向基類就調用基類,指向派生類就調用派生類對應的虛函數。

由于切片,導致我們傳遞給 Func 函數的每一個類都只有 Person 的那一部分,此時我們編譯器就是更具我們的虛函數表指針中的地址找到我們不同的函數位置,從而形成多態行為的。

這張圖就是我們 Person 類傳遞給 ptr,ptr 指向 Person,此時我們就會調用 Person 的 _vfptr 去找尋關于 BuyTicket 函數的指針。

這張圖是我們 Student 類傳遞給 ptr,在這之間進行了切片操作,然后?ptr 指向 Student 中屬于 Person 的那一部分,此時我們就會調用 Student 中的 Person 的 _vfptr 去找尋關于 BuyTicket 函數的指針。

(2)動態綁定與靜態綁定

  • 對不滿足多態條件(指針或者引用 + 調用虛函數)的函數調用是在編譯時綁定的,也就是編譯時確定調用函數的地址,叫做靜態綁定
  • 滿足多態條件的函數調用是在運行時綁定,也就是在運行時指向對象的虛函數表中找到調用函數的地址,也叫做動態綁定。

我們可以從匯編來看看。

// ptr是指針+BuyTicket是虛函數滿?多態條件。
// 這?就是動態綁定,編譯在運?時到ptr指向對象的虛函數表中確定調?函數地址
ptr->BuyTicket();
00EF2001 mov eax, dword ptr[ptr]
00EF2004 mov edx, dword ptr[eax]
00EF2006 mov esi, esp
00EF2008 mov ecx, dword ptr[ptr]
00EF200B mov eax, dword ptr[edx]
00EF200D call eax
// BuyTicket不是虛函數,不滿?多態條件。
// 這?就是靜態綁定,編譯器直接確定調?函數地址
ptr->BuyTicket();
00EA2C91 mov ecx, dword ptr[ptr]
00EA2C94 call Student::Student(0EA153Ch)

(3)虛函數表

  • 基類對象的虛函數表中存放基類所有的虛函數的地址。同類型的對象共用一張虛表,不同類型的對象各自有獨立的虛表,所以基類和派生類有各自獨立的虛表。
  • 派生類由兩部分構成,繼承下來的基類和自己的成員,一般情況下,繼承下來的基類中有虛函數表指針,自己就不會再生成虛函數表指針。但是要注意這里繼承下來的基類部分虛函數表指針和基類對象的虛函數表指針不是同一個,就像基類對象的成員和派生類對象中的基類對象成員也是相互獨立的。
  • 派生類中重寫基類的虛函數,派生類的虛函數表中對應的虛函數就會被覆蓋成派生類重寫的虛函數地址。
  • 派生類的虛函數表中包含:(1)基類的虛函數地址;(2)派生類重寫的虛函數地址完成覆蓋;(3)派生類自己的虛函數地址三個部分。
  • 虛函數表本質是一個存虛函數指針的指針數組,一般情況這個數組最后面放了一個0x00000000 標記(這個 C++并沒有進行規定,各個編譯器自行定義的,vs 系列編譯器會在后面放,g++系列編譯不會放)
  • 虛函數和普通函數一樣,編譯好后是一段指令,都是存在代碼段的,至少虛函數的地址又存在虛表中。
  • 虛函數表存放的位置嚴格來說并沒有標準答案 C++ 標準并沒有規定,在 vs 中存在代碼段(常量區)

我們每個基類和派生類如果有虛函數都有一張虛表,但是如果它們類型相同,那它們使用的虛表也是相同的。我們子類繼承的父類的虛函數表,但是它們不是同一個虛函數,而是獨立的。

如果我們的子類沒有重寫我們的父類,那我們子類的虛函數表就會在自己的虛函數表中從父類的虛函數表拷貝一份放到子類中屬于父類的那一部分虛函數表中。

如果我們有重寫,那我們重寫的內容就會把基類對應位置進行覆蓋,然后寫上自己的函數內容。


總結

以上便是我們多態的全部內容,繼承和多態的產生,使我們 C++ 的代碼的層次更加的多樣,在我們以后的大型項目中會經常遇見,我們這里一定得多多學習了解。

🎇堅持到這里已經很厲害啦,辛苦啦🎇

? ? ? ? ?

づ?ど

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

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

相關文章

Swift 語法學習指南 - 與 Kotlin 對比

Swift 語法學習指南 - 與 Kotlin 對比 本指南專為有 Android/Kotlin 開發經驗的開發者設計&#xff0c;通過對比學習快速掌握 Swift 語法 目錄 語言基礎對比變量與常量數據類型函數定義類與結構體繼承與協議可選類型集合類型控制流閉包與Lambda擴展與Extension錯誤處理內存管理…

嵌入式C語言筆記十七——構造數據類型

一.結構體&#xff1a;1.類型定義&#xff1a;struct 結構體名 {數據類型1 成員變量1;數據類型2 成員變量2;數據類型3 成員變量3;... };struct student {char name[32];char sex;int age;int score; };2.結構體變量定義&#xff1a;存儲類型 數據類型 變量名;3.結構體元素初始化…

深入實踐G1垃圾收集器調優:Java應用性能優化實戰指南

深入實踐G1垃圾收集器調優&#xff1a;Java應用性能優化實戰指南 一、技術背景與應用場景 隨著微服務和海量并發請求的普及&#xff0c;Java應用在生產環境中對低延遲和高吞吐的需求日益顯著。傳統的CMS和Parallel GC 在大內存場景下常出現Full GC 停頓時間長、吞吐下降等問題…

【JobScheduler】Android 后臺任務調度的核心組件指南

JobScheduler 是 Android 平臺上原生支持在直接啟動模式&#xff08;Direct Boot Mode&#xff09;下執行任務的調度器。 相比 WorkManager 需要復雜的配置才能勉強支持直接啟動&#xff0c;JobScheduler 在這方面有著天生的優勢和明確的 API 支持。如果你面臨的硬性要求是必須…

c# 調用basler 相機

目錄 一聯合halcon&#xff1a; 二 c# 原生 一聯合halcon&#xff1a; 環境配置 下載安裝pylon軟件 下載安裝halcon 創建 winform項目 test_basler 添加引用 打開pylon可以連接相機 可以看到我的相機id為23970642 &#xff08; c#聯合halcon的基礎教程&#xff08;案例…

《2025年AI產業發展十大趨勢報告》四十六

《2025年AI產業發展十大趨勢報告》四十六隨著科技的迅猛發展&#xff0c;人工智能&#xff08;AI&#xff09;作為引領新一輪科技革命和產業變革的戰略性技術&#xff0c;正逐步滲透到各個行業和領域&#xff0c;成為推動經濟社會發展的重要引擎。2023年&#xff0c;生成式AI的…

c++ 雜記

1. 為什么返回*this?2. 3. 友元函數的使用&#xff1a;需要頭文件中類內外聲明&#xff0c;cpp文件中實現定義哦// Sales_data.h #ifndef SALES_DATA_H #define SALES_DATA_H#include <string>class Sales_data {std::string bookNo;int units_sold 0;double revenue …

PDF文件基礎-計算機字體

計算機字體的原理包含了字符編碼、字形渲染和字體文件存儲三個關鍵技術。 字符編碼負責將每個字符映射到一個唯一的數字碼&#xff1b;字形渲染則將這些數字碼轉換成屏幕或紙張上可識別的圖形&#xff1b;字體文件存儲則包含了字符的編碼、圖形描述信息以及字體的其他屬性&…

華為IP(9)

OSPF的基本配置OSPF路由計算前言&#xff1a;1)同一區域內的OSPF路由器擁有完全一致的LSDB&#xff0c;在區域內部&#xff0c;OSPF采用SPF算法完成路由計算。2&#xff09;隨著網絡規模不斷擴大&#xff0c;路由器為了完成路由計算所消耗的內存、CPU資源也越來越多。通過區域劃…

java.nio.file.InvalidPathException異常

一.問題概述 本人在ubuntu22.04的操作系統上&#xff0c;運行java程序時創建一個文件時&#xff0c;由于文件名稱中包含了中文&#xff0c;所以導致了程序拋出了java.nio.file.InvalidPathException的異常。 java.nio.file.InvalidPathException: Malformed input or input co…

Next系統總結學習(一)

下面我按題號逐條 詳細 解釋并給出示例與最佳實踐。為便于閱讀&#xff0c;我會同時給出關鍵代碼片段&#xff08;偽代碼/實用例子&#xff09;&#xff0c;并指出常見坑與解決方案。 1. 你是如何理解服務端渲染&#xff08;SSR&#xff09;的&#xff1f;它的核心工作流程是怎…

房屋安全鑒定需要什么條件

房屋安全鑒定需要什么條件&#xff1a;專業流程與必備要素解析房屋安全鑒定是保障建筑使用安全的重要環節&#xff0c;它通過對建筑結構、材料性能及使用狀況的全面評估&#xff0c;為房屋的安全使用、改造或維護提供科學依據。隨著城市建筑老化及自然災害頻發&#xff0c;房屋…

現代C++:現代C++?

C語言正在走向完美&#xff0c;所以&#xff0c;C語言值得學習&#xff08;甚至研究&#xff09;&#xff0c;這些知識可以成為一切編程的基礎。然而在實踐中&#xff0c;不必全面的使用C語言的各種特性&#xff0c;而應根據工程項目的實際情況&#xff0c;適當取舍&#xff08…

【C++】哈希表實現

1. 哈希概念 哈希(hash)又稱散列&#xff0c;是?種組織數據的方式。從譯名來看&#xff0c;有散亂排列的意思。本質就是通過哈希 函數把關鍵字Key跟存儲位置建立一個映射關系&#xff0c;查找時通過這個哈希函數計算出Key存儲的位置&#xff0c;進行快速查找 1.1 直接定址法…

ai 玩游戲 llm玩街霸 大模型玩街霸 (3)

1. 開源代碼地址&#xff1a; https://github.com/OpenGenerativeAI/llm-colosseum 2. 架構&#xff1a; 3. 圖片&#xff1a; 4. 感覺還是下面的步驟&#xff1a; a. 實時理解游戲當前環境&#xff0c;英雄角色&#xff0c;英雄狀態 b. 根據當前狀態感知&#xff0c;生成英雄…

2025年滲透測試面試題總結-59(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 一、SQL注入全解 二、XSS與文件漏洞 三、服務端漏洞專題 四、職業經驗與能力評估 1、注入攻擊原理是什么…

GPT系列--類GPT2源碼剖析

無需多言&#xff0c;大家應該都用過了&#xff0c;如今都更新到GPT-5了。1. GPT-1回到2018年的NLP&#xff0c;神仙打架&#xff0c;BERT與GPT不分先后。GPT是“Generative Pre-Training”的簡稱&#xff0c;生成式的預訓練。BERT和GPT肯定是GPT難訓練&#xff0c;引用量也是B…

這是一款沒有任何限制的免費遠程手機控制手機的軟件

這是一款沒有任何限制的免費遠程手機控制手機的軟件支持安卓和蘋果1.安裝1.1被控制端安裝airdroid1.2控制端air mirror2.登錄賬號控制端和被控制端登錄同一個賬號3.控制打開控制端軟件選擇要控制的機器直接點“遠程控制“

Observability:更智能的告警來了:更快的分診、更清晰的分組和可操作的指導

作者&#xff1a;來自 Elastic Drew Post 探索 Elastic Stack 告警的最新增強功能&#xff0c;包括改進的相關告警分組、將儀表盤鏈接到告警規則&#xff0c;以及將調查指南嵌入到告警中。 在 9.1 版本中&#xff0c;我們對告警進行了重大升級&#xff0c;幫助 SRE 和運維人員更…

數智之光燃盛景 共同富裕創豐饒

8月29日&#xff0c;2025數博會“一帶一路”國際大數據產業發展暨數智賦能新時代、共同富裕向未來的會議在貴陽國際生態會議中心隆重舉行。作為全球大數據領域的重要盛會&#xff0c;此次活動吸引了來自聯合國機構、國際組織、科研院所、知名企業等社會各界的百余位代表&#x…