【C++】入門(一):命名空間、缺省參數、函數重載

目錄

一、關鍵字

二、命名空間

問題引入(問題代碼):

域的問題

1.::域作用限定符 的 用法:

2.域的分類

3.編譯器的搜索原則

命名空間的定義

命名空間的使用

舉個🌰栗子:

1.作用域限定符指定命名空間名稱

2. using 引入命名空間中的成員 即 展開命名空間中某一個

3. usinng namespace 命名空間名稱 展開命名空間

三、C++輸入、輸出

四、缺省參數

概念

全缺省參數

半缺省參數

實踐中的應用場景🌰舉個例子:

聲明和定義分離

回顧 聲明 和定義的概念

再來分析上述程序

理解編譯與鏈接的過程

理解函數與文件的關系

五、函數重載

代碼示例:

C++是如何支持函數重載的?

函數名修飾


一、關鍵字

asmdoifreturntrycontinue
autodoubleinlineshorttypedeffor
booldynamic_castintsignedtypeidpublic
breakelselongsizeoftypenamethrow
caseenummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigneddefault
charexternoperatorswitchvirtualregister
constfalseprivatetemplatevoidtrue
const_castfloatprotectedthisvolatilewhile
deletegotoreinterpret_cast

增加的關鍵字: C++增加了一些關鍵字來支持面向對象編程(如類、繼承、多態等)和模板編程。例如,class,public,protected,private,virtual,friend,template,typename等。這些關鍵字沒有在C語言中。

類型增強:C++增加了一些用于類型安全和方便的關鍵字,如bool,true,false,using,namespace等。

異常處理:為了支持異常處理,C++引入了try,catch,throw等關鍵字。

新的轉換操作符:C++提供了static_cast,dynamic_cast,const_cast和reinterpret_cast等關鍵字進行類型轉換,這是C語言中所沒有的。

增強的存儲類說明符:C++引入了mutable和thread_local等存儲類說明符。

模板編程:為了支持泛型編程,C++增加了template和typename關鍵字。

新增運算符:C++還定義了如new,delete等用于動態內存管理的關鍵字,這些在C中通常通過庫函數如malloc和free來實現。

特殊成員函數關鍵字:C++還有如default和delete等關鍵字,用于特殊成員函數的聲明,這樣設計是為了提供更好的控制。

二、命名空間

問題引入(問題代碼):

下面代碼存在命名沖突 : rand變量 和頭文件<stdlib.h>中聲明的函數 rand() 名字相同 導致沖突。

#include<stdio.h>
#include<stdlib.h>   /*rand*/
int rand = 0;
// C語言沒辦法解決類似這樣的命名沖突問題,所以C++提出了namespaceguan來解決
int main()
{printf("%d\n",rand);return 0;
}

域的問題

1.::域作用限定符 的 用法:

限定符左邊是哪一個域名 就限定了訪問該變量的范圍

左邊是空 默認是全局域

2.域的分類

  • 全局域

  • 局部域:如果不用限定符,默認訪問局部域 局部優先

  • 命名空間域:為了防止命名沖突 eg.全局定義兩個同名變量 ,防止重定義,C++提出就用關鍵字namespace把他們定義在不同命名空間域中。

  • 類域

    注意:

    全局域、局部域既會影響生命周期,也會影響訪問。命名空間只影響訪問

3.編譯器的搜索原則?

?1??當前局部域 2??全局域 3??如果指定了,直接去指定域搜索

命名空間的定義

正常定義

 // 正常的命名空間定義
namespace hhh
{// 命名空間中可以定義變量/函數/類型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}

嵌套定義

舉個栗子🌰:

namespace aaa
{namespace bbb{void Push(){cout<<"zs"<<endl;}}namespace ccc{void Push(){cout<<"yyy"<<endl;}}
}
int main()
{//嵌套定義在命名空間的同名函數 各自調用bit::bbb::Push();bit::ccc::Push();return 0;
}

ps:命名空間可以重名,編譯器會把他們合并,只要命名空間內部不沖突就可以

命名空間的使用

命名空間到底該如何使用?

舉個🌰栗子:

namespace yyy
{//命名空間中定義 變量 / 函數 /類型int a = 0;int b= 1;int Add(int left,int right){return left+right;}struct Node{struct Node* next;int val;};
}

1.作用域限定符指定命名空間名稱

//指定訪問
int main()
{//::作用域限定符printf("%d\n",yyy::a);return 0;
}

2. using 引入命名空間中的成員 即 展開命名空間中某一個

//展開一個
using yyy::b;
int main()
{printf("%d\n",yyy::a);//不可以 因為此時只展開了一個成員變量printf("%d\n",b);
}

3. usinng namespace 命名空間名稱 展開命名空間

展開命名空間 影響的是 域的搜索規則。不展開命名空間,默認情況編譯器只會在局部域、全局域搜索。展開命名空間就可以在命名空間里搜索。

//展開全部
using namespace yyy;
int main()
{printf("%d\n",yyy::a);//指定去該命名空間找變量aprintf("%d\n",b)
}

注意:

1. 日常練習展開為了方便使用可以展開std,實際工程實踐中慎重使用!

2.展開命名空間 不是 等同于引入全局變量!

3.展開命名空間 跟 包含頭文件 也有本質區別,包含頭文件 在預處理過程中本質是拷貝頭文件的內容

三、C++輸入、輸出

解釋Hello world代碼

//包含標準輸入輸出流庫
#include<iostream>
// std是C++標準庫的命名空間名,C++將標準庫的定義實現都放到這個命名空間中
using namespace std;int main(){//cout和cin是全局的流對象,細說分別是ostream和istream類型的對象// <<是流插入運算符,>>是流提取運算符//endl是C++符號,表示endline換行//他們都包含在包含<iostream>頭文件中cout<<"Hello world!!!"<<endl;return 0;}

說明:使用cout標準輸出對象(控制臺)和cin標準輸入對象(鍵盤)時,必須包含< iostream >頭文件 以及按命名空間使用方法使用std。

補充:std命名空間的使用習慣

1.日常練習:直接展開 using namespace std

2.項目開發:std::cout 使用時指定命名空間 + using std::cout 展開常用庫對象

C++ 輸入輸出 自動識別變量類型?

  • 示例代碼:
    #include <iostream>
    using namespace std;
    int main()
    {int a;double b;char c;// 可以自動識別變量的類型cin>>a;cin>>b>>c;cout<<a<<endl;cout<<b<<"  "<<c<<endl;return 0;
    }
    • 說明

cin>>a;這行代碼從標準輸入流(鍵盤)中接受一個整數,并將其存儲在變量a中。cin會根據提供的變量類型自動解釋輸入數據。cin>>b>>c;這行代碼首先從標準輸入流中接收一個雙精度浮點數,并將其存儲在變量b中,然后接收一個字符并存儲在c中。?

四、缺省參數

  • 概念

    聲明或定義函數時為函數的參數指定缺省值。缺省值就是給形參設置一個默認值。調用函數時,如果沒有指定實參,則使用參數的默認值。

    缺省值必須是 常量或者全局變量。一般使用常量。

    void Func(int a = 0)
    {cout<<a<<endl;
    }
    int main()
    {Func(); ? ?//沒有傳參 使用參數默認值 Func(10); ?//傳參時 使用指定的實參return 0;
    }
  • 全缺省參數

    void Func(int a = 10, int b = 20, int c = 30)
    {cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
    }
    調用Func()時,可以這樣給參數int main()
    {Func(1,2,3);Func(1,2);Func(1);Func();//注意:不可以跳越傳值//Func(,1,2);return 0;
    }
  • 半缺省參數

    注意:只能從右往左連續給缺省值,這樣調用保證傳的實參順序不存在歧義

    void Func(int a, int b = 20, int c = 30)
    {cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
    }
    //調用 同樣不能跳越給
    int main()
    {Func(1,2,3);Func(1,2);Func(1);
    }

實踐中的應用場景🌰舉個例子:

struct Stack
{int* a;int size;int capacity;//...
};
//StackInit()改造為半缺省函數 使得可以適用更多的需要開辟空間的場景
void StackInit(struct Stack* ps,int n=4)
{ps->a=(int*)malloc(sizeof(int)*n);
}
int main()
{struct Stack st1;//缺省參數 使得函數可以適應不同場景 // 1、確定要插入100個數據StackInit(&st1, 100); ?// call StackInit(?)
?// 2、只插入10個數據struct Stack st2;StackInit(&st2, 10); ? // call StackInit(?)
?// 3、不知道要插入多少個 //這時就可以使用函數定義里提供的 參數缺省值 //不知道插入多少個 可以先初始化四個空間struct Stack st3;StackInit(&st3);
?return 0;
}
  • 聲明和定義分離

    回顧 聲明 和定義的概念

    • 函數聲明告訴編譯器函數的名稱、返回類型以及參數列表(類型、順序和數量),但不涉及函數的具體實現。函數聲明經常出現在頭文件(.h)中

    • 函數定義:提供了函數的實際實現,它包括函數的主體,即函數被調用時將執行的具體代碼。函數定義包含了函數聲明的所有信息,并加上了函數體

    //Stack.h 聲明
    struct Stack
    {int* a;int size;int capacity;//...
    };
    void StackInit(struct Stack* ps,int n=4);//*注意 必須在聲明中給出缺省值
    void StackPush(struct Stack* ps,int x);
    //Stack.cpp 定義
    void StackInit(struct Stack* ps,int n)//*注意聲明和定義中缺省值不能同時給
    {ps->a=(int*)malloc(sizeof(int)*n);
    }
    void StackPush(struct Stack* ps , int x)
    {}
    //Test.cpp
    #include"Stack.h"
    int main()
    {struct Stack st1;// 1、確定要插入100個數據StackInit(&st1, 100); ?// call StackInit(?)//此時包含了頭文件,Test.cpp只有函數聲明 用這個函數的名字找到該函數的地址 編譯階段會檢查調用該函數是否存在匹配的函數,經過檢查 匹配// 2、只插入10個數據struct Stack st2;StackInit(&st2, 10); ? // call StackInit(?)// 3、不知道要插入多少個 struct Stack st3;StackInit(&st3);return 0;
    }

    但是試想一下,1??如果缺省值只在函數定義中給出,編譯階段 無法用這個函數的名字找到該函數的匹配 ,因為調用傳參跟函數聲明并不匹配。另一種情況,2??如果在函數的聲明和定義中都指定了缺省參數編譯器也可能不確定應該使用哪個版本的默認值為了避免這種情況,C++標準規定了缺省參數應當只在一個地方指定:

    • 如果函數聲明在頭文件中進行,那么就在頭文件中的聲明處指定缺省參數

    • 如果函數沒有在頭文件中聲明(例如,完全在一個.cpp文件內定義),那么就在函數定義處指定缺省參數

    綜上,

    1??在項目中,聲明和定義應當分離,缺省值一定要在函數聲明中給出!因為,編譯階段只有函數聲明,從而保證編譯階段是沒有問題的。

    2??聲明和定義分離,導致編譯階段無法找到函數的定義,沒有函數的地址。

  • 再來分析上述程序

    • 理解編譯與鏈接的過程

      1??預處理階段 :展開頭文件、宏替換、條件編譯、刪除注釋

      對于每個.c文件,編譯過程從預處理開始。預處理器會處理以#開頭的指令,例如#include "stack.h"會將stack.h中的內容文本上粘貼到stack.ctest.c文件中,這樣stack.ctest.c就可以看到這些函數聲明了

      2??編譯:檢查語法??生成匯編代碼

      編譯器接著編譯每個.c源文件,將它們轉換成目標代碼(通常是機器代碼的一種中間形態,稱為目標文件,擴展名為.o或.obj)。此時,編譯器確保源代碼符合語法規則,對每個源文件進行類型檢查,確保所有函數調用都符合其聲明,但還不解決跨文件的函數引用問題。例如,stack.c被編譯成stack.o,test.c被編譯成test.o

      3??匯編:匯編代碼??二進制機器碼

      4??鏈接:合并、有些地方要用函數名去其他文件找函數地址

      一旦所有的源文件被編譯成目標文件,鏈接器(linker)負責將這些目標文件以及必要的庫文件鏈接成一個單一的可執行文件。在鏈接過程中,如果test.c(對應的是test.o)調用了stack.c中(對應的是stack.o)的函數,鏈接器負責“修補”這些調用,使得test.o中的調用可以正確地連接到stack.o中定義的函數上,鏈接器確保所有外部引用都能正確解析到它們所引用的實體。

    • 理解函數與文件的關系

      • 在stack.h中聲明的函數,讓其他源文件知道這些函數的存在、它們的參數以及返回值類型。stack.h扮演了接口的角色。

      • stack.c提供了stack.h中聲明的函數的具體實現。test.c作為使用這些函數的客戶端代碼,通過#include "stack.h"能夠調用這些函數。

      • 編譯過程中,test.c和stack.c分別被編譯成中間的目標文件。這些目標文件中的函數調用尚未解析到具體的地址

      • 在鏈接過程,鏈接器解析這些調用,使得從test.o中的調用可以正確地定位到stack.o中的函數定義,從而生成一個完整的可執行文件,所有的函數調用都被正確地解析和連接,這個地址修正的過程也叫做重定位

五、函數重載

C語言不允許同名函數

C++允許同名函數。要求:函數名相同,參數不同,構成 函數重載

函數重載:是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數個數類型類型順序)不同,常用來處理實現功能類似但數據類型不同的問題。

代碼示例:

#include<iostream>using namespace std;// 1、參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}// 2、參數個數不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}// 3、參數類型順序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, 'a');f('a', 10);return 0;
}

C語言不支持重載 鏈接時,直接用函數名去找地址,有同名函數的情況則區分不開。

  • C++是如何支持函數重載的?

    通過函數名修飾實現的,只要函數參數不同,函數名就會被修飾成不同。然后直接用修飾好的名字,去找該函數的地址。

    • 函數名修飾

      名字修飾是編譯器自動進行的一種處理過程,它將C++源代碼中的函數名和變量名轉換成包含更多信息的唯一標識符。這些信息通常包括函數的參數類型、參數數量等,甚至可能包括所屬的類名(對于類成員函數),通過這種方式,每個重載的函數都會被賦予一個獨一無二的名字,確保鏈接器在最后鏈接程序的時候能夠區分它們

Linux下g++的修飾規則簡單易懂,下面我們使 用了g++演示了這個修飾后的名字。 通過下面我們可以看出gcc的函數修飾后名字不變。而g++的函數修飾后變成【_Z+函數長度 +函數名+類型首字母】。

  • 采用C語言編譯器編譯后結果

?

結論:在linux下,采用gcc編譯完成后,函數名字的修飾沒有發生改變。

  • 采用C++編譯器編譯后結果

?

結論:在linux下,采用g++編譯完成后,函數名字的修飾發生改變,編譯器將函數參數類型信息添加到修改后的名字中。

通過以上這里就理解了C語言沒辦法支持重載,因為同名函數沒辦法區分。而C++是通過函數修 飾規則來區分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。

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

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

相關文章

【數據結構與算法 | 堆篇】JAVA實現小頂堆

1. 堆的特點 堆的邏輯結構是數組&#xff0c;內存結構是完全二叉樹.完全二叉樹即只有最后一層才有葉子節點.堆又分為大頂堆與小頂堆. 大頂堆的特點是 : 父親節點比孩子節點的都要大. 小頂堆的特點與其相反.Java的優先級隊列(PriorityQueue)的底層實現即用到了小頂堆. 所以下文…

K210視覺識別模塊學習筆記3:內存卡寫入拍攝圖片_LED三色燈的操作_按鍵操作_定時器的配置使用

今日開始學習K210視覺識別模塊: LED三色燈的操作_按鍵操作_定時器的配置使用_內存卡寫入拍攝圖片 亞博智能的K210視覺識別模塊...... 本文最終目的是編寫一個按鍵拍照的例程序&#xff1a; 為以后的專用場景的模型訓練做準備&#xff0c;因為訓練自己的模型需要大量的圖片&a…

jmeter基礎入門練習題

jmeter存在A,B兩個線程組的情況下&#xff0c;默認設置下&#xff0c;運行順序是&#xff1a;A A&#xff1a;A,B同時運行 B&#xff1a;先運行A&#xff0c;在運行B C&#xff1a;先運行A&#xff0c;等待2s運行B D:先A運行完&#xff0c;等待默認設置時間后運行B 下列說法正…

編譯安裝PHP服務(LAMP3)

目錄 1.初始化設置&#xff0c;將安裝PHP所需軟件包傳到/opt目錄下 &#xff08;1&#xff09;關閉防火墻 &#xff08;2&#xff09;上傳軟件包到/opt目錄 2.安裝GD庫和GD庫關聯程序&#xff0c;用來處理和生成圖片 3.配置軟件模塊 4.編譯及安裝 5.優化把PHP 的可執行程…

nginx的安裝001

Nginx是一款高性能的HTTP和反向代理服務器&#xff0c;以及郵件代理服務器&#xff0c;由 Igor Sysoev 開發并公開發布于2004年。Nginx以其高并發處理能力、低內存消耗和穩定性著稱&#xff0c;特別適合部署在高流量的網站上。 操作系統&#xff1a; CentOS Stream 9 安裝步驟…

【算法訓練 day44 分割等和子集】

目錄 一、分割等和子集-LeetCode 416思路實現代碼1.二維dp代碼2.一維dp代碼 問題總結 一、分割等和子集-LeetCode 416 Leecode鏈接: leetcode 416 文章鏈接: 代碼隨想錄 視頻鏈接: B站 給你一個 只包含正整數 的 非空 數組 nums 。請你判斷是否可以將這個數組分割成兩個子集&…

SQL入門教程,很詳細

SQL&#xff08;Structured Query Language&#xff09;是一種用于管理關系數據庫的標準語言。它被廣泛用于存儲、操作和檢索數據。在這篇文章中&#xff0c;我們將介紹SQL的基本概念和常用命令。 首先&#xff0c;我們需要了解SQL的基本結構。SQL語句通常由以下幾個部分組成&…

頭歌數據結構與算法課程設計易-算式運算的合法性

給定一個算式運算&#xff0c;算式由運算數、、-、、/、(、)組成&#xff0c;請編寫程序判斷該算式運算是否合法。如果合法&#xff0c;計算該算式的值。 輸入描述&#xff1a; 第一行輸入一個運算表達式 輸出描述&#xff1a; 如果表達式合法則計算其值&#xff0c;結果保留兩…

c語言之向文件讀寫數據塊

c語言需要向文件讀寫數據塊需要用到fread語句和fwrite語句 fread語句的語法格式 fread(butter,size,count,fp) butter&#xff1a;讀取的數據存入內存地址 size:讀取的字節大小 count:讀取數據的個數 fp:讀取的文件指針 fwrite語句語法格式 fwrite(butter,size,count,fp…

企業如何利用社交媒體二維碼做宣傳?提升品牌形象

和普通的二維碼不同&#xff0c;社交媒體二維碼可以通過一個二維碼鏈接企業的超過16的社交媒體渠道鏈接&#xff0c;包括&#xff1a;企業官網、小程序、公眾號、淘寶店鋪、抖音鏈接、小紅書鏈接、美團鏈接、餓了么鏈接…等等。掃描之后&#xff0c;可以在這個社交媒體二維碼界…

校園志愿者|基于SprinBoot+vue的校園志愿者管理系統(源碼+數據庫+文檔)

校園志愿者管理系統 目錄 基于SprinBootvue的校園志愿者管理系統 一、前言 二、系統設計 三、系統功能設計 1 系統功能模塊 2管理員功能 3志愿者功能 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博主介紹&a…

采購訂單審批和取消例子

文章目錄 1 Introduction2 Example 1 Introduction This is a exmaple for releaseing po and reseting po. 2 Example DATA:lw_in TYPE zmms015,lw_out TYPE zmms015_out,lt_head LIKE TABLE OF ZMMT003_head,lw_head TYPE ZMMT003_head,lt_item TYPE zmmt003_item_t,lt…

12.RedHat認證-Linux文件系統(下)

12.RedHat認證-Linux文件系統(下) swap虛擬內存 我加一個硬盤做實驗sdc # 創建交換分區&#xff08;不用做成邏輯卷也能靈活分區&#xff09; [rootcentos8 ~]# fdisk /dev/sdc -l Disk /dev/sdc&#xff1a;10 GiB&#xff0c;10737418240 字節&#xff0c;20971520 個扇區 …

REX 521饋線保護繼電器提供 您的高效中壓網絡 保護、測量、監控和基本 控制功能

REX 521饋線保護繼電器提供 您的高效中壓網絡 保護、測量、監控和基本 控制功能。典型的REX 521應用包括輸入和輸出饋線 在隔離中性點中&#xff0c;諧振接地&#xff0c;牢固 接地和電阻接地系統。 …完善ABB繼電器解決方案系列 這種最先進的保護繼電器補充了ABB的一系列解決方…

深入理解linux文件系統與日志分析

深入理解linux文件系統與日志分析 linux文件系統: 文件是存儲在硬盤上的&#xff0c;硬盤上的最小存儲單位是扇區&#xff0c;每個扇區的大小是512字節。 inode&#xff1a;元信息&#xff08;文件的屬性 權限&#xff0c;創建者&#xff0c;創建日期等等&#xff09; block…

【AVL Design Explorer DOE】

AVL Design Explorer DOE 1、關于DOE的個人理解2、DOE參考資料-知乎2.1 DOE發展及基本類型2.2 DOE應用場景2.3 Mintab 中的 DOE工具3、AVL Design Explorer DOE示例 1、關于DOE的個人理解 仿真和試驗一樣&#xff0c;就像盲人摸象&#xff0c;在不知道大象的全景之前&#xff…

Java 垃圾回收

一、概述 GC GC(Garbage Collection)&#xff0c;在程序運行過程中內存空間是有限的&#xff0c;為了更好的的使用有限的內存空間&#xff0c;GC會將不再使用的對象清除然后將其所占用的內存釋放出來。 java的垃圾回收機制 Java的垃圾收集&#xff08;Garbage Collection, …

嵌入式Linux復制剪切刪除指令詳解

指令操作 1. cp 復制指令 a. 用法&#xff1a;cp [ 選項 ] [ 源文件或目錄 ] [ 目標文件或目錄 ]&#xff1b; b. 用途&#xff1a;用于復制文件或目錄&#xff1b; c. 通常情況下&#xff0c;復制的都不是空文件夾&#xff0c;所以直接使用 cp 復制空文件會失敗&#xff0…

創建Django項目及應用

1 創建Project 1個Project可以對應多個app django-admin startproject myproject 2 創建App python manage.py startapp app01 INSTALLED_APPS [# ...app01,app02,# ... ] 如果要讓這個應用在項目中起作用&#xff0c;需要在項目的 settings.py 文件的 INSTALLED_APPS 配置…

java中成員內部類、局部內部類、匿名內部類各自的特點

成員內部類&#xff1a;定義在類的內部&#xff0c;方法的外部&#xff0c;成員內部類作為外部類的成員&#xff0c;可以直接訪問外部類的私有屬性。 局部內部類&#xff1a;定義在方法的內部&#xff0c;對于局部內部類我們常常使用一個方法&#xff0c;得到一個接口實現類的…