導航:首頁 > 源碼編譯 > 重寫編譯器

重寫編譯器

發布時間:2022-08-19 07:37:25

1. C語言為什麼可以重寫標准庫函數

這個問題是一個好問題,我之前也沒思索過或者嘗試過,
首先我們弄清楚一件事,函數聲明可以放在任何頭文件,實現可以放在任何實現該函數的源文件中,那麼就存在一個問題:
編譯時,到底優先去使用哪一個,為什麼沒有把標准庫中的函數擴展過來;在windows下標准庫被編譯成了msvcr120.dll(msvcr100.dll,這里指release版),所以並不是擴展到代碼中,而是在調用時動態鏈接;
而題主在其中自定義文件中實現了該函數,所以編譯時找到了該函數的實現,並不會去鏈接dll(這應該是編譯器做的一些工作,確定系統的dll需要載入哪些),所以題主的程序執行時就只有一份fputc了,並不沖突。
題主可以通過快捷鍵跳轉聲明就知道了,VS下,點選fputc實現函數按F12跳轉到聲明,指向的是stdio.h,再按一次跳轉到你自己的定義了。Qt的話使用F2。
大概就是這樣子了,可追問。

2. 誰能詳細告訴我有關java中的方法重載和重寫有什麼區別

public void speak(){
System.out.println("Father");}}public class Son extends Father{
public void speak(){
System.out.println("son");}}這也叫做多態性,重寫方法只能存在於具有繼承關系的類中,重寫方法只能重寫父類非私有的方法,當上例中Father類的speak()方法為private時,Son類不能重寫speak()方法,此時在Son類中定義的speak()方法相當於一個新的方法,與Father中的speak()方法沒有任何關系.
當Father類的speak()方法聲明為final時,無論該方法的修飾符是public,protected還是默認,Son類根本不能重寫speak()方法,試圖編譯代碼時,編譯器會報錯.例:
public class Father{
final void speak(){
System.out.println("Father");}}public class Son extends Father{
public void speak(){
System.out.println("son");}}//編譯器錯誤信息:Son 中的 speak() 無法覆蓋 Father 中的 speak();被覆蓋的方法為 final
重寫方法的規則:
1.參數列表必須完全與被重寫的方法的相同,否則不能稱其為重寫而是重載;
2.返回的類型必須一直與被重寫的方法的返回類型相同,否則不能稱其為重寫而是重載;
3.訪問修飾符的限制一定要大於被重寫方法的訪問修飾符(public>protected>default>private);
4.重寫方法一定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常.例如:
父類的一個方法申明了一個檢查異常IOException,在重寫這個方法時就不能拋出Exception,只能拋出IOException的子類異常,可以拋出非檢查異常(unchecked exception,如運行時異常).
重載方法的規則:
1.必須具有不同的參數列表;
2.可以有不同的返回類型,只要參數列表不同就可以了;
3.可以有不同的訪問修飾符;

3. 預處理指令#pragma db code是什麼意思

一、作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和 C++語言完全兼容的情況下,給出主機或操作系統專有的特徵。依據定義,編譯指示是機器或操作系統專有的,且對於每個編譯器都是不同的。

二、常用的pragma指令的詳細解釋。
1.#pragma once。保證所在文件只會被包含一次,它是基於磁碟文件的,而#ifndef則是基於宏的。
2.#pragma warning。允許有選擇性的修改編譯器的警告消息的行為。有如下用法:
#pragma warning(disable:4507 34; once:4385; error:164) 等價於:
#pragma warning(disable:4507 34) // 不顯示4507和34號警告信息
#pragma warning(once:4385) // 4385號警告信息僅報告一次
#pragma warning(error:164) // 把164號警告信息作為一個錯誤
#pragma warning(default:176) // 重置編譯器的176號警告行為到默認狀態
同時這個pragma warning也支持如下格式,其中n代表一個警告等級(1---4):
#pragma warning(push) // 保存所有警告信息的現有的警告狀態
#pragma warning(push,n) // 保存所有警告信息的現有的警告狀態,並設置全局報警級別為n
#pragma warning(pop) // 恢叢 鵲木 孀刺 趐ush和pop之間所做的一切改動將取消
例如:
#pragma warning(push)
#pragma warning(disable:4705)
#pragma warning(disable:4706)
#pragma warning(disable:4707)
#pragma warning(pop)
在這段代碼後,恢復所有的警告信息(包括4705,4706和4707)。
3.#pragma hdrstop。表示預編譯頭文件到此為止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以 加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁碟空間,所以使用這個選項排除一些頭文 件。
4.#pragma message。在標准輸出設備中輸出指定文本信息而不結束程序運行。用法如下:
#pragma message("消息文本")。當編譯器遇到這條指令時就在編譯輸出窗口中將「消息文本」列印出來。
5.#pragma data_seg。一般用於DLL中,它能夠設置程序中的初始化變數在obj文件中所在的數據段。如果未指定參數,初始化變數將放置在默認數據段.data中,有如下用法:
#pragma data_seg("Shared") // 定義了數據段"Shared",其中有兩個變數a和b
int a = 0; // 存儲在數據段"Shared"中
int b; // 存儲在數據段".bss"中,因為沒有初始化
#pragma data_seg() // 表示數據段"Shared"結束,該行代碼為可選的
對變數進行專門的初始化是很重要的,否則編譯器將把它們放在普通的未初始化數據段中而不是放在shared中。如上述的變數b其實是放在了未初始化數據段.bss中。
#pragma data_seg("Shared")
int j = 0; // 存儲在數據段"Shared"中
#pragma data_seg(push, stack1, "Shared2") //定義數據段Shared2,並將該記錄賦予別名stack1,然後放入內部編譯器棧中
int l = 0; // 存儲在數據段"Shared2"中
#pragma data_seg(pop, stack1) // 從內部編譯器棧中彈出記錄,直到彈出stack1,如果沒有stack1,則不做任何操作
int m = 0; // 存儲在數據段"Shared"中,如果沒有上述pop段,則該變數將儲在數據段"Shared2"中
6.#pragma code_seg。它能夠設置程序中的函數在obj文件中所在的代碼段。如果未指定參數,函數將放置在默認代碼段.text中,有如下用法:
void func1() { // 默認存儲在代碼段.text中
}
#pragma code_seg(".my_data1")
void func2() { // 存儲在代碼段.my_data1中
}
#pragma code_seg(push, r1, ".my_data2")
void func3() { // 存儲在代碼段.my_data2中
}
#pragma code_seg(pop, r1)
void func4() { // 存儲在代碼段.my_data1中
}
7.#pragma pack。用來改變編譯器的位元組對齊方式。常規用法為:
#pragma pack(n) //將編譯器的位元組對齊方式設為n,n的取值一般為1、2、4、8、16,一般默認為8
#pragma pack(show) //以警告信息的方式將當前的位元組對齊方式輸出
#pragma pack(push) //將當前的位元組對齊方式放入到內部編譯器棧中
#pragma pack(push,4) //將位元組對齊方式4放入到內部編譯器棧中,並將當前的內存對齊方式設置為4
#pragma pack(pop) //將內部編譯器棧頂的記錄彈出,並將其作為當前的內存對齊方式
#pragma pack(pop,4) //將內部編譯器棧頂的記錄彈出,並將4作為當前的內存對齊方式
#pragma pack(pop,r1) //r1為自定義的標識符,將內部編譯器中的記錄彈出,直到彈出r1,並將r1的值作為當前的內存對齊方式;如果r1不存在,當不做任何操作
8.#pragma comment。將一個注釋記錄放置到對象文件或可執行文件中。
其格式為:#pragma comment( comment-type [,"commentstring"] )。其中,comment-type是一個預定義的標識符,指定注釋的類型,應該是compiler,exestr,lib,linker,user之一。
compiler:放置編譯器的版本或者名字到一個對象文件,該選項是被linker忽略的。
exestr:在以後的版本將被取消。
lib:放置一個庫搜索記錄到對象文件中,這個類型應該與commentstring(指定Linker要搜索的lib的名稱和路徑)所指定的庫類型一致。在對象文件中,庫的名字跟在默認搜索記錄後面;linker搜索這個這個庫就像你在命令行輸入這個命令一樣。你可以在一個源文件中設置多個庫搜索記錄,它們在obj文件中出現的順序與在源文件中出現的順序一樣。
如果默認庫和附加庫的次序是需要區別的,使用/Zl編譯開關可防止默認庫放到object模塊中。
linker:指定一個連接選項,這樣就不用在命令行輸入或者在開發環境中設置了。只有下面的linker選項能被傳給Linker:
/DEFAULTLIB
/EXPORT
/INCLUDE
/MANIFESTDEPENDENCY
/MERGE
/SECTION
(1)/DEFAULTLIB:library
/DEFAULTLIB選項將一個library添加到LINK在解析引用時搜索的庫列表。用/DEFAULTLIB指定的庫在命令行上指定的庫之後和obj文件中指定的默認庫之前被搜索。
忽略所有默認庫(/NODEFAULTLIB)選項重寫/DEFAULTLIB:library。如果在兩者中指定了相同的library名稱,忽略庫(/NODEFAULTLIB:library)選項將重寫/DEFAULTLIB:library。
(2)/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]
使用該選項,可以從程序導出函數以便其他程序可以調用該函數,也可以導出數據。通常在DLL中定義導出。
entryname是調用程序要使用的函數或數據項的名稱。ordinal為導出表的索引,取值范圍在1至65535;如果沒有指定ordinal,則LINK將分配一個。NONAME關鍵字只將函數導出為序號,沒有entryname。DATA 關鍵字指定導出項為數據項。客戶程序中的數據項必須用extern __declspec(dllimport)來聲明。
有三種導出定義的方法,按照建議的使用順序依次為:
源代碼中的__declspec(dllexport)
.def文件中的EXPORTS語句
LINK命令中的/EXPORT規范
所有這三種方法可以用在同一個程序中。LINK在生成包含導出的程序時還要創建導入庫,除非在生成過程中使用了.exp 文件。
LINK使用標識符的修飾形式。編譯器在創建obj文件時修飾標識符。如果entryname以其未修飾的形式指定給鏈接器(與其在源代碼中一樣),則LINK將試圖匹配該名稱。如果無法找到唯一的匹配名稱,則LINK發出錯誤信息。當需要將標識符指定給鏈接器時,請使用Dumpbin工具獲取該標識符的修飾名形式。
(3)/INCLUDE:symbol
/INCLUDE選項通知鏈接器將指定的符號添加到符號表。若要指定多個符號,請在符號名稱之間鍵入逗號(,)、分號(;)或空格。在命令行上,對每個符號需指定一次/INCLUDE:symbol。
鏈接器通過將包含符號定義的對象添加到程序來解析symbol。該功能對於添加不會鏈接到程序的庫對象非常有用。
用該選項所指定的符號將覆蓋通過/OPT:REF對該符號進行的移除操作。
(4)/MANIFESTDEPENDENCY:manifest_dependency
/MANIFESTDEPENDENCY允許你指定位於manifest文件的段的屬性。/MANIFESTDEPENDENCY信息可以通過下面兩種方式傳遞給LINK:
直接在命令行運行/MANIFESTDEPENDENCY
通過#pragma comment
(5)/MERGE:from=to
/MERGE選項將第一個段(from)與第二個段(to)進行聯合,並將聯合後的段命名為to的名稱。
如果第二個段不存在,LINK將段(from)重命名為to的名稱。
/MERGE選項對於創建VxDs和重寫編譯器生成的段名非常有用。
(6)/SECTION:name,[[!]{DEKPRSW}][,ALIGN=#]
/SECTION選項用來改變段的屬性,當指定段所在的obj文件編譯的時候重寫段的屬性集。
可移植的可執行文件(PE)中的段(section)與新可執行文件(NE)中的節區(segment)或資源大致相同。
段(section)中包含代碼或數據。與節區(segment)不同的是,段(section)是沒有大小限制的連續內存塊。有些段中的代碼或數據是你的程序直接定義和使用的,而有些數據段是鏈接器和庫管理器(lib.exe)創建的,並且包含了對操作系統來說很重要的信息。
/SECTION選項中的name是大小寫敏感的。
不要使用以下名稱,因為它們與標准名稱會沖突,例如,.sdata是RISC平台使用的。
.arch
.bss
.data
.edata
.idata
.pdata
.rdata
.reloc
.rsrc
.sbss
.sdata
.srdata
.text
.xdata
為段指定一個或多個屬性。屬性不是大小寫敏感的。對於一個段,你必須將希望它具有的屬性都進行指定;如果某個屬性未指定,則認為是不具備這個屬性。如果你未指定R,W或E,則已存在的讀,寫或可執行狀態將不發生改變。
要對某個屬性取否定意義,只需要在屬性前加感嘆號(!)。
E:可執行的
R:可讀取的
W:可寫的
S:對於載入該段的鏡像的所有進程是共享的
D:可廢棄的
K:不可緩存的
P:不可分頁的
注意K和P是表示否定含義的。
PE文件中的段如果沒有E,R或W屬性集,則該段是無效的。
ALIGN=#選項讓你為一個具體的段指定對齊值。
user:放置一個常規注釋到一個對象文件中,該選項是被linker忽略的。
9.#pragma section。創建一個段。
其格式為:#pragma section( "section-name" [, attributes] )
section-name是必選項,用於指定段的名字。該名字不能與標准段的名字想沖突。可用/SECTION查看標准段的名稱列表。
attributes是可選項,用於指定段的屬性。可用屬性如下,多個屬性間用逗號(,)隔開:
read:可讀取的
write:可寫的
execute:可執行的
shared:對於載入該段的鏡像的所有進程是共享的
nopage:不可分頁的,主要用於Win32的設備驅動程序中
nocache:不可緩存的,主要用於Win32的設備驅動程序中
discard:可廢棄的,主要用於Win32的設備驅動程序中
remove:非內存常駐的,僅用於虛擬設備驅動(VxD)中
如果未指定屬性,默認屬性為read和write。
在創建了段之後,還要使用__declspec(allocate)將代碼或數據放入段中。
例如:
//pragma_section.cpp
#pragma section("mysec",read,write)
int j = 0;
__declspec(allocate("mysec"))
int i = 0;
int main(){}
該例中, 創建了段"mysec",設置了read,write屬性。但是j沒有放入到該段中,而是放入了默認的數據段中,因為它沒有使用__declspec(allocate)進行聲明;而i放入了該段中,因為使用__declspec(allocate)進行了聲明。
10.#pragma push_macro與#pragma pop_macro。前者將指定的宏壓入棧中,相當於暫時存儲,以備以後使用;後者將棧頂的宏出棧,彈出的宏將覆蓋當前名稱相同的宏。例如:
#include
#define X 1
#define Y 2
int main() {
printf("%d",X);
printf("\n%d",Y);
#define Y 3 // C4005
#pragma push_macro("Y")
#pragma push_macro("X")
printf("\n%d",X);
#define X 2 // C4005
printf("\n%d",X);
#pragma pop_macro("X")
printf("\n%d",X);
#pragma pop_macro("Y")
printf("\n%d",Y);
}
輸出結果:
1
2
1
2
1
3

4. 編譯器錯誤消息: CS0115: 「ASP.XXX.aspx.GetTypeHashCode()」: 沒有找到適合的方法來重寫

就是當前類的 GetTypeHashCode() 方法不存在或者不允許Override。

5. C#重載,C#重寫和C#隱藏的區別

重載:相同的函數名稱,參數列表不同的函數,可以根據不同的參數來實現不同的邏輯。

重寫:子類重寫基類的虛函數,這樣不同的子類就可以以不同的方式實現同一個功能。比如定義一個鳥類基類,在基類中定義一個飛翔的虛函數,實現子類燕子與蜂鳥,顯然燕子與蜂鳥的飛行方式是不同的,這時就可以用到重寫了。

隱藏:子類使用new字定義欄位、屬性或函數,則子類的該欄位、屬性或函數隱藏了基類的同名欄位、屬性或函數。

可以在VS里運行以下代碼,通過查看輸出來加深理解:

publicclassBaseClass{
///<summary>
///重載函數1
///</summary>
///<returns></returns>
publicstringGetString(){
return"我是重載函數1";
}

///<summary>
///重載函數2
///</summary>
///<paramname="obj"></param>
///<returns></returns>
publicstringGetString(objectobj){
return"我是重載函數2";
}

///<summary>
///待重寫的函數
///</summary>
(){
return"我是基類待重寫的函數";
}

///<summary>
///被隱藏的函數
///</summary>
///<returns></returns>
publicstringHideFunction(){
return"我是基類的被隱藏函數";
}
}

publicclassChildClass:BaseClass{
///<summary>
///子類重寫函數
///</summary>
///<returns></returns>
(){
return"我是子類重寫的函數";
}

///<summary>
///隱藏基類函數
///</summary>
///<returns></returns>
publicnewstringHideFunction(){
return"我是子類的函數,我隱藏了基類HideFunction";
}
}

staticvoidMain(string[]args){
ChildClasschild=newChildClass();
Console.WriteLine("ChildClass.GetString():"+child.GetString());
Console.WriteLine("ChildClass.GetString(objectobj):"+child.GetString(newobject()));
Console.WriteLine("ChildClass.OverideFunction():"+child.OverideFunction());
Console.WriteLine("ChildClass.HideFunction():"+child.HideFunction());

BaseClassbaseClass=childasBaseClass;
Console.WriteLine("BaseClass.OverideFunction():"+baseClass.OverideFunction());
Console.WriteLine("BaseClass.HideFunction():"+baseClass.HideFunction());

baseClass=newBaseClass();
Console.WriteLine("BaseClass.OverideFunction():"+baseClass.OverideFunction());
}

6. Java中的重載與重寫有什麼區別(請舉兩個簡單的例子)

Java中的重載與重寫的區別:

首先講講:重載(Overloading)

(1) 方法重載是讓類以統一的方式處理不同類型數據的一種手段。多個同名函數同時存在,具有不同的參數個數/類型。
重載Overloading是一個類中多態性的一種表現。

(2) Java的方法重載,就是在類中可以創建多個方法,它們具有相同的名字,但具有不同的參數和不同的定義。
調用方法時通過傳遞給它們的不同參數個數和參數類型來決定具體使用哪個方法, 這就是多態性。

(3) 重載的時候,方法名要一樣,但是參數類型和個數不一樣,返回值類型可以相同也可以不相同。無法以返回型別作為重載函數的區分標准。

下面是重載的例子:
package c04.answer;//這是包名
//這是這個程序的第一種編程方法,在main方法中先創建一個Dog類實例,然後在Dog類的構造方法中利用this關鍵字調用不同的bark方法。
不同的重載方法bark是根據其參數類型的不同而區分的。
//注意:除構造器以外,編譯器禁止在其他任何地方中調用構造器。
package c04.answer;
public class Dog {
Dog()
{
this.bark();
}
void bark()//bark()方法是重載方法
{
System.out.println(\"no barking!\");
this.bark(\"female\", 3.4);
}
void bark(String m,double l)//注意:重載的方法的返回值都是一樣的,
{
System.out.println(\"a barking dog!\");
this.bark(5, \"China\");
}
void bark(int a,String n)//不能以返回值區分重載方法,而只能以「參數類型」和「類名」來區分
{
System.out.println(\"a howling dog\");
}
public static void main(String[] args)
{
Dog dog = new Dog();
//dog.bark(); [Page]
//dog.bark(\"male\", \"yellow\");
//dog.bark(5, \"China\");

然後談談:重寫(Overriding)

(1) 父類與子類之間的多態性,對父類的函數進行重新定義。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。在Java中,子類可繼承父類中的方法,而不需要重新編寫相同的方法。
但有時子類並不想原封不動地繼承父類的方法,而是想作一定的修改,這就需要採用方法的重寫。
方法重寫又稱方法覆蓋。

(2)若子類中的方法與父類中的某一方法具有相同的方法名、返回類型和參數表,則新方法將覆蓋原有的方法。
如需父類中原有的方法,可使用super關鍵字,該關鍵字引用了當前類的父類。

(3)子類函數的訪問修飾許可權不能少於父類的;
下面是重寫的例子:

概念:即調用對象方法的機制。

動態綁定的內幕:
1、編譯器檢查對象聲明的類型和方法名,從而獲取所有候選方法。試著把上例Base類的test注釋掉,這時再編譯就無法通過。

2、重載決策:編譯器檢查方法調用的參數類型,從上述候選方法選出唯一的那一個(其間會有隱含類型轉化)。
如果編譯器找到多於一個或者沒找到,此時編譯器就會報錯。試著把上例Base類的test(byte b)注釋掉,這時運行結果是1 1。

3、若方法類型為priavte static final ,java採用靜態編譯,編譯器會准確知道該調用哪
個方法。

4、當程序運行並且使用動態綁定來調用一個方法時,那麼虛擬機必須調用對象的實際類型相匹配的方法版本。
在例子中,b所指向的實際類型是TestOverriding,所以b.test(0)調用子類的test。
但是,子類並沒有重寫test(byte b),所以b.test((byte)0)調用的是父類的test(byte b)。
如果把父類的(byte b)注釋掉,則通過第二步隱含類型轉化為int,最終調用的是子類的test(int i)。

總結:
多態性是面向對象編程的一種特性,和方法無關,
簡單說,就是同樣的一個方法能夠根據輸入數據的不同,做出不同的處理,即方法的
重載——有不同的參數列表(靜態多態性)
而當子類繼承自父類的相同方法,輸入數據一樣,但要做出有別於父類的響應時,你就要覆蓋父類方法,
即在子類中重寫該方法——相同參數,不同實現(動態多態性)

OOP三大特性:繼承,多態,封裝。
public class Base
{
void test(int i)
{
System.out.print(i);
}
void test(byte b)
{
System.out.print(b);
}
}
public class TestOverriding extends Base
{
void test(int i)
{
i++;
System.out.println(i);
}
public static void main(String[]agrs)
{
Base b=new TestOverriding();
b.test(0)
b.test((byte)0)
}
}

這時的輸出結果是1 0,這是運行時動態綁定的結果。

重寫的主要優點是能夠定義某個子類特有的特徵:
public class Father{
public void speak(){
System.out.println(Father);
}
}
public class Son extends Father{
public void speak(){
System.out.println("son");
}
}
這也叫做多態性,重寫方法只能存在於具有繼承關系中,重寫方法只能重寫父類非私有的方法。
當上例中Father類speak()方法被private時,Son類不能重寫出Father類speak()方法,此時Son類speak()方法相當與在Son類中定義的一個speak()方法。
Father類speak()方法一但被final時,無論該方法被public,protected及默認所修飾時,Son類根本不能重寫Father類speak()方法,
試圖編譯代碼時,編譯器會報錯。例:
public class Father{
final public void speak(){
System.out.println("Father");
}
}
public class Son extends Father{
public void speak(){
System.out.println("son");
}
} //編譯器會報錯;

Father類speak()方法被默認修飾時,只能在同一包中,被其子類被重寫,如果不在同一包則不能重寫。
Father類speak()方法被protoeted時,不僅在同一包中,被其子類被重寫,還可以不同包的子類重寫。

重寫方法的規則:
1、參數列表必須完全與被重寫的方法相同,否則不能稱其為重寫而是重載。
2、返回的類型必須一直與被重寫的方法的返回類型相同,否則不能稱其為重寫而是重載。
3、訪問修飾符的限制一定要大於被重寫方法的訪問修飾符(public>protected>default>private)
4、重寫方法一定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常。例如:
父類的一個方法申明了一個檢查異常IOException,在重寫這個方法是就不能拋出Exception,只能拋出IOException的子類異常,可以拋出非檢查異常。

而重載的規則:
1、必須具有不同的參數列表;
2、可以有不責罵的返回類型,只要參數列表不同就可以了;
3、可以有不同的訪問修飾符;
4、可以拋出不同的異常;

重寫與重載的區別在於:
重寫多態性起作用,對調用被重載過的方法可以大大減少代碼的輸入量,同一個方法名只要往裡面傳遞不同的參數就可以擁有不同的功能或返回值。
用好重寫和重載可以設計一個結構清晰而簡潔的類,可以說重寫和重載在編寫代碼過程中的作用非同一般.

7. (急!!!)go語言編譯器如何重寫

你還是找個qq群問一下

8. 為什麼Finalize不能重寫

不能重寫只是編譯器乾的事兒,實際上它就是想讓你只能寫析構函數,而寫析構函數就相當於重寫了Finalize。
這只是一種設計方式。

你可以改Assembly的代碼,直接寫IL,會發現是可以override的。

你可以上網搜索一下Mono.cecil,有相關的內容。
舉個例子,按照C#的規范,一個類裡面是不可能有兩個方法名相同,參數相同,而僅僅是返回值不同的方法的,例如:
public class MyClass
{
public string Foo()
{
}

public int Foo()
{
}
}
直接寫上述C#代碼,編譯無法通過,編譯器會告訴你這兩個方法簽名是不明確的。
但是如果是IL(即Assembly中編譯後的內容),是可以有這么兩個方法的。
因為IL中描述的方法其實就是個方法的地址,根本不管你的方法是叫啥名字。只要知道地址,就能調用。

================================
再補充一下,你可以自己做這個實驗:
首先寫一個類:
public class SampleClass
{
public SampleClass()
{
}

~SampleClass()
{
}
}
編譯。
然後使用Visual Studio自帶的工具IL Disassembler,一般位於開始菜單的 Microsoft VisualStudio 2010(看你自己的安裝版本)\Microsoft Windows SDK Tools\ 下面
打開剛才編譯好的dll或者exe,在左邊會有一個類型列表,找到剛才寫的SampleClass,點開"+",會看到下面有兩個方法,
一個是:.ctor:void() 這個其實就是構造函數
另一個是:Finalize:void() 這個就是我們剛才寫的析構函數。
這說明什麼呢?說明其實析構函數就是重載的Finalize。也就是編譯器給我們玩兒的小把戲而已。
再雙擊這個Finalize:void(),可以看到裡面的IL代碼:
.method family hidebysig virtual instance void
Finalize() cil managed
{
// Code size 14 (0xe)
.maxstack 1
.try
{
IL_0000: nop
IL_0001: nop
IL_0002: leave.s IL_000c
} // end .try
finally
{
IL_0004: ldarg.0
IL_0005: call instance void [mscorlib]System.Object::Finalize()
IL_000a: nop
IL_000b: endfinally
} // end handler
IL_000c: nop
IL_000d: ret
} // end of method SampleClass::Finalize

注意這一句:
[mscorlib]System.Object::Finalize()
IL_000a: nop
IL_000b: endfinally
其實就是在調用基類的(即object)的Finalize方法

9. 這是什麼編譯器

匯編。這真的是最早最早的。准確的來說,這和編譯器的開發有關,不用說太細,很麻煩怕你不懂。你現在假設第一個編譯器是用會變寫出來的,它的功能很簡單,就是解釋簡單一種類似於C語言的高級語言,但是這種所謂的高級語言還沒有完全擁有C語言的所有特性。只有比較簡單核心功能,比如能把文本文件的高級語言轉換成機器代碼並且執行。有了這個原型之後,就可以用這個編譯器來解釋簡單C程序,就可以用C重寫編寫一個新的編譯器,這樣就有更多的C的功能。於是,從此之後就用現有的編譯器解釋更復雜的語言,用更復雜的語言寫出更好的編譯器,然後不斷這樣迭代。這確實是編譯器的演變。然後最後一個問題就是當一個新的CPU發明過後,怎麼辦,需要重寫又從匯編開始寫編譯器嗎?答案是不用。假設你有一個CPU A執行一些代碼,你用匯編寫了一個基礎的C編譯器,然後用C寫出了更復雜的編譯器,接受更復雜的C功能,然後不斷循環演化。現在你有了CPU B,CPU B和CPU A執行兩套完全不同的代碼,那如何讓CPU B的機器也可以變異C語言呢?因為現在A上面已經可以運行非常復雜的C語言程序了,所以你可以在A上面開發一個編譯器把C語言程序轉化為CPU B的執行代碼。然後用這個程序,直接編譯你的C語言編譯器,再把這個程序轉換到有B命令集的電腦上面,這樣你就開發出了B電腦需要的C語言編譯器。所以除非你真的是活在非常早起的人類。否在現在的編譯器基本上都利用這種原理直接編譯已經用C語言或者其它高級語言寫好的代碼來產生新的編譯器就行了。理論上可以只使用C語言來開發C的編譯器,不過處於一些歷史原因和底層效率等因素的考量,部分代碼還是使用匯編來實現的。我舉得不過是一個例子,不一定是真實的C語言編譯的進化,何況有這么多不同的C語言編譯器,每一個的發展歷史都有小的不同。但是基本上都是利用了這種編譯器編譯新的編譯器的思想來實現了。而這樣回溯回去,最早的編譯器只能使用匯編來些。而其實最早的匯編語言的編譯器就只能使用機器語言來寫了。不過都是先處理簡單的轉換任務,有了這個核心功能過後,就可以寫程序轉換更復雜的語法。然後越來越復雜。就有了各種各樣的高級語言編譯器了。

閱讀全文

與重寫編譯器相關的資料

熱點內容
查看手機谷歌伺服器地址 瀏覽:191
python操作zookeeper 瀏覽:705
蘋果手機dcim文件夾顯示不出來 瀏覽:430
如何壓縮文件夾聯想電腦 瀏覽:583
程序員的學習之旅 瀏覽:440
apkdb反編譯 瀏覽:922
雪花演算法為什麼要二進制 瀏覽:825
在文檔中打開命令行工具 瀏覽:608
android圖標尺寸規范 瀏覽:369
python實用工具 瀏覽:208
流量計pdf 瀏覽:936
科東加密認證價格 瀏覽:532
dos命令讀文件 瀏覽:996
成為程序員需要什麼學歷 瀏覽:674
pdf農葯 瀏覽:229
canal加密 瀏覽:497
日本安卓系統和中國有什麼區別 瀏覽:137
linux命令行修改文件 瀏覽:838
從編譯和解釋的角度看 瀏覽:650
徐志摩pdf 瀏覽:652