【C++】:多態

朋友們、伙計們,我們又見面了,本期來給大家解讀一下有關多態的知識點,如果看完之后對你有一定的啟發,那么請留下你的三連,祝大家心想事成!

C 語 言 專 欄:C語言:從入門到精通

數據結構專欄:數據結構

個? 人? 主? 頁?:stackY、

C + + 專 欄? ?:C++

Linux 專?欄? :Linux

目錄

1. 多態的概念

1.1 概念

2. 多態的定義及實現

2.1 多態的構成條件

2.2 虛函數

2.3 虛函數的重寫

2.3.1 虛函數重寫的兩個例外?

2.4?C++11 override 和 final

2.5 重載、重寫(覆蓋)、重定義(隱藏)的對比

3. 抽象類?

3.1 概念

3.2 接口繼承和實現繼承

4. 多態的原理

4.1 虛函數表?

4.2 多態的原理

4.3 動態綁定與靜態綁定

5. 單繼承和多繼承關系的虛函數表

5.1 單繼承中的虛表

5.2 多繼承中的虛函數表?

6. 總結


1. 多態的概念

1.1 概念

多態的概念:通俗來說,就是多種形態,具體點就是去完成某個行為,當不同的對象去完成時會產生出不同的狀態。

?
舉個栗子:比如買票這個行為,當普通人買票時,是全價買票;學生買票時,是半價買票;軍人買票時是優先買票。

2. 多態的定義及實現

2.1 多態的構成條件

多態是在不同繼承關系的類對象,去調用同一函數,產生了不同的行為。比如Student繼承了
Person。Person對象買票全價,Student對象買票半價。
那么在繼承中要構成多態還有兩個條件:

  • 1. 必須通過基類的指針或者引用調用虛函數
  • 2. 被調用的函數必須是虛函數,且派生類必須對基類的虛函數進行重寫
    class Person
    {
    public:virtual void BuyTicket(){cout << "票價->全價" << endl;}
    };
    class Student : public Person
    {
    public:virtual void BuyTicket(){cout << "票價->半價" << endl;}
    };
    void Func(Person& p)
    {p.BuyTicket();
    }int main()
    {Person p;Func(p);Student s;Func(s);return 0;
    }

    ??

2.2 虛函數

虛函數:即被virtual修飾的類成員函數稱為虛函數。

class Person
{
public:virtual void BuyTicket(){cout << "票價->全價" << endl;}
};

2.3 虛函數的重寫

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

class Person
{
public:virtual void BuyTicket(){cout << "票價->全價" << endl;}
};
class Student : public Person
{
public://void BuyTicket()virtual void BuyTicket(){cout << "票價->半價" << endl;}
};

注意:

在重寫基類虛函數時,派生類的虛函數在不加virtual關鍵字時,雖然也可以構成重寫(因為繼承后基類的虛函數被繼承下來了在派生類依舊保持虛函數屬性),但是該種寫法不是很規范,不建議這樣使用。

2.3.1 虛函數重寫的兩個例外?

1. 協變(基類與派生類虛函數返回值類型不同)
派生類重寫基類虛函數時,與基類虛函數返回值類型不同。即基類虛函數返回基類對象的指
針或者引用,派生類虛函數返回派生類對象的指針或者引用時,稱為協變。
?

class A
{};
class B :public A
{};class Person
{
public:virtual A* BuyTicket(){cout << "票價->全價" << endl;return nullptr;}
};
class Student : public Person
{
public://void BuyTicket()virtual B* BuyTicket(){cout << "票價->半價" << endl;return nullptr;}
};

2. 析構函數的重寫(基類與派生類析構函數的名字不同)
如果基類的析構函數為虛函數,此時派生類析構函數只要定義,無論是否加virtual關鍵字,
都與基類的析構函數構成重寫,雖然基類與派生類析構函數名字不同。

雖然函數名不相同,看起來違背了重寫的規則,其實不然,這里可以理解為編譯器對析構函數的名稱做了特殊處理,編譯后析構函數的名稱統一處理成estructor。

class Person
{
public:virtual ~Person(){ cout << "~Person()" << endl; }
};
class Student : public Person
{
public:virtual ~Student() { cout << "~Student()" << endl; }//~Student(){ cout << "~Student()" << endl; }
};int main()
{Person* p = new Person;Student* s = new Student;delete p;delete s;return 0;
}

2.4?C++11 override 和 final

從上面可以看出,C++對函數重寫的要求比較嚴格,但是有些情況下由于疏忽,可能會導致函數名字母次序寫反而無法構成重寫,而這種錯誤在編譯期間是不會報出的,只有在程序運行時才能發現端倪,因此:C++11提供了override和final兩個關鍵字,可以幫助用戶檢測是否重寫。

1. final:修飾虛函數,表示該虛函數不能再被重寫

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

??

拓展:

final修飾類,則該類不能被繼承

class A final
{
public:int _a;
};
class B final :public A
{
public:int _b;
};
class C : public B
{
public:int _c;
};

??

2. override: 檢查派生類虛函數是否重寫了基類某個虛函數,如果沒有重寫編譯報錯。

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

??

2.5 重載、重寫(覆蓋)、重定義(隱藏)的對比

3. 抽象類?

3.1 概念

在虛函數的后面寫上 =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;}
};void TestCar()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();
}

3.2 接口繼承和實現繼承

普通函數的繼承是一種實現繼承,派生類繼承了基類函數,可以使用函數,繼承的是函數的實現。虛函數的繼承是一種接口繼承,派生類繼承的是基類虛函數的接口,目的是為了重寫,達成多態,繼承的是接口。所以如果不實現多態,不要把函數定義成虛函數。

4. 多態的原理

首先先來看一下這道題:

// 這里常考一道筆試題:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};

根據之前的判斷,成員函數在公共代碼區,所以這個類的大小只有4字節,如果我們打印出來看一下的話并不是4字節,而是8字節,那這是為什么呢?我們可以調試來觀察一波:

通過調試可以觀察到在b里面還存在一個__vfptr,這個玩意叫做虛函數表,到底有什么用呢?這就是我們接下來的話題:

4.1 虛函數表?

通過上面的觀察可以發現在Base對象中的這個指針我們叫做虛函數表指針(v代表virtual,f代表function)。一個含有虛函數的類中都至少都有一個虛函數表指針,因為虛函數的地址要被放到虛函數表中,虛函數表也簡稱虛表。那么派生類中這個表放了些什么呢?我們接著往下分析

我們可以將上面的代碼進行改造一下:

// 1.我們增加一個派生類Derive去繼承Base
// 2.Derive中重寫Func1
// 3.Base再增加一個虛函數Func2和一個普通函數Func3
class Base
{
public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};
class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};

通過觀察可以發現:

  • 1. 派生類對象d中也有一個虛表指針,d對象由兩部分構成,一部分是父類繼承下來的成員,虛表指針也就是存在部分的另一部分是自己的成員。
  • 2. 基類b對象和派生類d對象虛表是不一樣的,這里我們發現Func1完成了重寫,所以d的虛表中存的是重寫的Derive::Func1,所以虛函數的重寫也叫作覆蓋,覆蓋就是指虛表中虛函數的覆蓋。重寫是語法的叫法,覆蓋是原理層的叫法。
  • 3. 另外Func2繼承下來后是虛函數,所以放進了虛表,Func3也繼承下來了,但是不是虛函數,所以不會放進虛表。
  • 4. 虛函數表本質是一個存虛函數指針的指針數組,一般情況這個數組最后面放了一個nullptr。
  • 5. 總結一下派生類的虛表生成:a.先將基類中的虛表內容拷貝一份到派生類虛表中 b.如果派生類重寫了基類中某個虛函數,用派生類自己的虛函數覆蓋虛表中基類的虛函數 c.派生類自己新增加的虛函數按其在派生類中的聲明次序增加到派生類虛表的最后。
  • 6. 這里還有存在一個很容易混淆的問題:虛函數存在哪的?虛表存在哪的? 注意虛表存的是虛函數指針,不是虛函數,虛函數和普通函數一樣的,都是存在代碼段的,只是他的指針又存到了虛表中。另外對象中存的不是虛表,存的是虛表指針。那么虛表存在哪的呢?實際我們去驗證一下會發現vs下是存在代碼段的。

4.2 多態的原理

在前面的代碼中我們關于買票做了區分,在那里Func函數傳Person調用Person::BuyTicket(全價),傳Student調用的是Student::BuyTicket(半價)

  • 1. 觀察上圖的紅色箭頭我們看到,p是指向基類對象時,p->BuyTicket在基類的虛表中找到虛函數是Person::BuyTicket。
  • 2. 觀察上圖的紫色箭頭我們看到,p是指向派生類對象時,p->BuyTicket在派生類的虛表中找到虛函數是Student::BuyTicket。
  • 3. 這樣就實現出了不同對象去完成同一行為時,展現出不同的形態。
  • 4. 反過來思考我們要達到多態,有兩個條件,一個是虛函數覆蓋,一個是對象的指針或引用調用虛函數。
  • 5. 再通過下面的匯編代碼分析,看出滿足多態以后的函數調用,不是在編譯時確定的,是運行起來以后到對象的中取找的。不滿足多態的函數調用時編譯時確認好的。

4.3 動態綁定與靜態綁定

1. 靜態綁定又稱為前期綁定(早綁定),在程序編譯期間確定了程序的行為,也稱為靜態多態,比如:函數重載。
2. 動態綁定又稱后期綁定(晚綁定),是在程序運行期間,根據具體拿到的類型確定程序的具體行為,調用具體的函數,也稱為動態多態。
3. 本小節之前(4.2小節)買票的匯編代碼很好的解釋了什么是靜態(編譯器)綁定和動態(運行時)綁定

5. 單繼承和多繼承關系的虛函數表

5.1 單繼承中的虛表

//單繼承
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};
class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b;
};

通過觀察監視窗口中我們發現看不見func3和func4。這里是編譯器的監視窗口故意隱藏了這兩個函數,也可以認為是他的一個小bug。那么我們如何查看d的虛表呢?下面我們使用代碼打印出虛表中的函數。

typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{// 依次取虛表中的虛函數指針打印并調用。調用就可以看出存的是哪個函數cout << " 虛表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d個虛函數地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Base b;Derive d;//1.先取b的地址,強轉成一個int*的指針//2.再解引用取值,就取到了b對象頭4bytes的值,這個值就是指向虛表的指針//3.再強轉成VFPTR*,因為虛表就是一個存VFPTR類型(虛函數指針類型)的數組。//4.虛表指針傳遞給PrintVTable進行打印虛表VFPTR * vTableb = (VFPTR*)(*(int*)&b);PrintVTable(vTableb);VFPTR* vTabled = (VFPTR*)(*(int*)&d);PrintVTable(vTabled);return 0;
}

思路:取出b、d對象的頭4bytes,就是虛表的指針,前面我們說了虛函數表本質是一個存虛函數指針的指針數組,這個數組最后面放了一個nullptr。

  • 1.先取b的地址,強轉成一個int*的指針
  • 2.再解引用取值,就取到了b對象頭4bytes的值,這個值就是指向虛表的指針
  • 3.再強轉成VFPTR*,因為虛表就是一個存VFPTR類型(虛函數指針類型)的數組。
  • 4.虛表指針傳遞給PrintVTable進行打印虛表
  • 5.需要說明的是這個打印虛表的代碼經常會崩潰,因為編譯器有時對虛表的處理不干凈,虛表最后面沒有放nullptr,導致越界,這是編譯器的問題。我們只需要點目錄欄的-生成-清理解決方案,再編譯就好了

5.2 多繼承中的虛函數表?

//多繼承
class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{cout << " 虛表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d個虛函數地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Derive d;VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);PrintVTable(vTableb1);VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));PrintVTable(vTableb2);return 0;
}

觀察上圖可以看出:多繼承派生類的未重寫的虛函數放在第一個繼承基類部分的虛函數表中

6. 總結

1. 什么是多態?

多態分為靜態多態動態多態,靜態多態在編譯時就已經確定好了,動態多態在運行時才會確定,常見靜態多態是函數重載,動態多態則是通過父類的指針或者引用調用虛函數,對基類虛函數的重寫,指向誰,就調用誰的虛函數,以此來實現多種形態。

2.?什么是重載、重寫(覆蓋)、重定義(隱藏)?


重載:同一作用域內,函數名相同參數不同(參數類型/數量/順序)。
重寫:子類和父類的虛函數,名稱、返回值(協變例外)、參數都相同,叫做子類重寫了父類的虛函數。
重定義:子類和父類的函數名相同,稱子類隱藏了父類的某個函數。?

3.? 多態的原理

父類和子類之中保存的虛表指針是不一樣的,通過傳入指針或者引用確定去子類還是父類中去尋找虛表指針,最后達到調用不同虛函數的目的。

4.?inline函數可以是虛函數嗎?

可以,在VS之下,如果構成多態,編譯器會放棄inline的屬性,這個函數就不是內聯函數了,因為內聯函數會被展開,是沒有地址的。而虛函數會將其地址放入至虛表之中。

普通調用:inline起作用;多態調用:inline不起作用。

5. 靜態成員可以是虛函數嗎?


不可以,編譯報錯,因為靜態成員函數沒有this指針,使用類型::成員函數的調用方式無法訪問虛函數表,但是它可以指定類域調用,所以靜態成員函數無法放進虛函數表。

6.?構造函數可以是虛函數嗎?


不可以,編譯報錯,對象中虛表指針是構造函數初始化列表階段才初始化的。
虛函數多態調用,要到虛表中找,但是虛表指針還未初始化。

7. 析構函數可以是虛函數嗎?

最好將析構函數定義成虛函數,否則會出現指向子類對象的父類指針調用父類析構函數而不會調用子類虛構函數的情況,出現內存泄漏。

8.?對象訪問普通函數快還是虛函數更快?


首先如果是普通對象,是一樣快的。如果是指針對象或者是引用對象,則調用的普通函數快,因為構成多態,運行時調用虛函數需要到虛函數表中去查找。

9.?虛函數表是在什么階段生成的,存在哪的?

虛函數表是在編譯階段就生成的,一般情況下存在代碼段(常量區)的。

10.?C++菱形繼承的問題?虛繼承的原理?

菱形繼承因為子類對象當中會有兩份父類的成員,因此會導致數據冗余和二義性的問題。

通過虛基表中的偏移量來找到對應的成員,從而解決了數據冗余和二義性的問題。

11.?什么是抽象類?抽象類的作用?

抽象類不能實例化出對象,抽象類強制重寫了虛函數

另外抽象類體現出了接口繼承關系。

?

朋友們、伙計們,美好的時光總是短暫的,我們本期的的分享就到此結束,欲知后事如何,請聽下回分解~,最后看完別忘了留下你們彌足珍貴的三連喔,感謝大家的支持!????

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

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

相關文章

Linux(CentOS7)上安裝mysql

在CentOS中默認安裝有MariaDB&#xff08;MySQL的一個分支&#xff09;&#xff0c;可先移除/卸載MariaDB。 yum remove mariadb // 查看是否存在mariadb rpm -qa|grep -i mariadb // 卸載 mariadb rpm -e --nodeps rpm -qa|grep mariadb yum安裝 下載rpm // 5.6版本 wge…

XML映射文件

<?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace"org.mybatis.example.BlogMapper&q…

conan 入門(三十二):package_info中配置禁用CMakeDeps生成使用項目自己生成的config.cmake

conanfile.py中定義的package_info()方法用于向package的調用者(conumer)提供包庫名&#xff0c;編譯/連接選項&#xff0c;文件夾等等信息&#xff0c;有了這些信息構建工具的generator就可以根據它們生成對應的文件&#xff0c;用于調用者引用package. 比如基于cmake的CMakeD…

線索二叉樹:C++實現

引言&#xff1a; 線索二叉樹是一種特殊的二叉樹&#xff0c;它可以通過線索&#xff08;線索是指在二叉樹中將空指針改為指向前驅或后繼的指針&#xff09;的方式將二叉樹轉化為一個線性結構&#xff0c;從而方便對二叉樹進行遍歷。本文將介紹如何使用C實現線索二叉樹。 技術…

安全地公網訪問樹莓派等設備的服務 內網穿透--frp 23年11月方法

如果想要樹莓派可以被公網訪問&#xff0c;可以選擇直接網上搜內網穿透提供商&#xff0c;一個月大概10塊錢&#xff0c;也有免費的&#xff0c;但是免費的速度就不要希望很好了。 也可以選擇接下來介紹的frp&#xff0c;這種方式不需要付費&#xff0c;但是需要你有一臺有著公…

vue3自定義拖拽指令

<template><div v-move class"box"></div> </template><script setup lang"ts"> import { Directive } from vue const vMove:Directive (el:HTMLElement) >{const mousedown (e:MouseEvent) >{// 鼠標按下const s…

【Golang】解決使用interface{}解析json數字會變成科學計數法的問題

在使用解析json結構體的時候&#xff0c;使用interface{}接數字會發現變成了科學計數法格式的數字&#xff0c;不符合實際場景的使用要求。 舉例代碼如下&#xff1a; type JsonUnmStruct struct {Id interface{} json:"id"Name string json:"name"…

Linux 的性能調優的思路

Linux操作系統是一個開源產品&#xff0c;也是一個開源軟件的實踐和應用平臺&#xff0c;在這個平臺下有無數的開源軟件支撐&#xff0c;我們常見的apache、tomcat、mysql等。 開源軟件的最大理念是自由、開放&#xff0c;那么Linux作為一個開源平臺&#xff0c;最終要實現的是…

Java反射調用kotlin中的類,Object類,Companion對

Java反射調用kotlin中的類&#xff0c;Object類&#xff0c;Companion對象 1. Java反射調用kotlin中的普通類 kotlin普通類&#xff1a; package com.common; class TestNormal {fun get():String{return "Nolmal abc"}fun showNum(v:Int){println("Nolmal s…

uniApp微信支付實現

后端&#xff1a;小程序下單 - 小程序支付 | 微信支付商戶文檔中心 服務端需要請求&#xff1a;https://api.mch.weixin.qq.com該地址獲取微信支付Api接口需要的參數。 服務端請求接口需要的Body參數&#xff1a; 客戶端&#xff08;前端&#xff09;需要調用&#xff1a;wx.…

12V降3.3V100mA穩壓芯片WT7133

12V降3.3V100mA穩壓芯片WT7133 WT71XX系列是一款采用CMOS工藝實現的三端高輸入電壓、低壓差、小輸出電流電壓穩壓器。 它的輸出電流可達到100mA&#xff0c;輸入電壓可達到18V。其固定輸出電壓的范圍是2.5V&#xff5e;8.0V&#xff0c;用戶 也可通過外圍應用電路來實現可變電壓…

加載minio中存儲的靜態文件html,不顯示樣式與js

問題描述:點擊鏈接獲取的就是純靜態文件,但是通過瀏覽器可以看到明明加載了css文件與js文件 原因:仔細看你會發現加載css文件顯示的contentType:text/html文件,原來是minio上傳文件時將所有文件的contentType設置成了text/html 要在上傳時指定文件,根據文章的類型指定的Conten…

css 固定按鈕到頁面頂部或者底部的實現方式

實現方式 要將按鈕固定到頂部或底部&#xff0c;可以使用CSS的定位屬性來實現。下面是一種常用的方法&#xff1a; 創建一個包含按鈕的HTML元素&#xff0c;例如一個<div>元素。確保給它添加一個唯一的id&#xff0c;以便在CSS中進行定位。 <div id"myButton&qu…

從二極管到linux服務器

軟件設計: os: 批處理系統: 輪詢系統:單片機裸機開發 實時系統:ucosii,rtos,rt-thread、風和系統、liteos(主要是海思系列soc在用)等 非實時系統:linux 對os任務切換時寄存器的功能有理解。 對ipc機制有理解。 bsp: 需要對寄存器、單片機內部總線、iic、spi、uart、c…

win10開機黑屏只有鼠標?這份指南幫你輕松解決!

win10是一個出色的操作系統&#xff0c;但有時用戶可能會遇到開機后只有鼠標顯示在屏幕上的問題&#xff0c;這種情況可能會讓人感到困惑和沮喪。在本文中&#xff0c;我們將介紹三種解決win10開機黑屏只有鼠標的方法&#xff0c;以幫助您快速恢復正常的桌面環境。 方法1&#…

Ubuntu18.4中安裝wkhtmltopdf + Odoo16配置【二】

deepin Linux 安裝wkhtmltopdf 1、先從官網的鏈接里下載linux對應的包 wkhtmltopdf/wkhtmltopdf 下載需要的版本&#xff0c;推薦版本&#xff0c;新測有效&#xff1a; wkhtmltox-0.12.4_linux-generic-amd64.tar.xz 2、解壓下載的文件 解壓后會有一個wkhtmltox文件夾 3…

CTA-GAN:基于生成對抗性網絡的主動脈和頸動脈非集中CT血管造影 CT到增強CT的合成技術

Generative Adversarial Network–based Noncontrast CT Angiography for Aorta and Carotid Arteries 基于生成對抗性網絡的主動脈和頸動脈非集中CT血管造影背景貢獻實驗方法損失函數Thinking 基于生成對抗性網絡的主動脈和頸動脈非集中CT血管造影 https://github.com/ying-f…

可自行DIY單TYPE-C接口設備實現DRP+OTG功能芯片

隨著USB-C接口的普及&#xff0c;歐盟的法律法規強制越來越多的設備開始采用這種接口。由于 USB-C接口的高效性和便攜性&#xff0c;使各種設備之間的連接和數據傳輸變得非常方便快捷&#xff0c;它們不僅提供了強大的功能&#xff0c;還為我們的日常生活和工作帶來了極大的便利…

Python與設計模式--代理模式

5-Python與設計模式–代理模式 一、網絡服務器配置白名單 代理模式是一種使用頻率非常高的模式&#xff0c;在多個著名的開源軟件和當前多個著名的互聯網產品后 臺程序中都有所應用。下面我們用一個抽象化的簡單例子&#xff0c;來說明代理模式。 首先&#xff0c;構造一個網絡…

ssm+vue的企業文檔管理系統(有報告)。Javaee項目,ssm vue前后端分離項目。

演示視頻&#xff1a; ssmvue的企業文檔管理系統&#xff08;有報告&#xff09;。Javaee項目&#xff0c;ssm vue前后端分離項目。 項目介紹&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三層體系結構&…