C++
出自 MBA智库百科(https://wiki.mbalib.com/)
目錄 |
C++是在C語言的基礎上開發的一種面向對象編程語言,屬於編譯型語言,應用廣泛;C++支持多種編程範式 --面向對象編程、泛型編程和過程化編程。最新正式標準C++於2014年8月18日公佈。其編程領域眾廣,常用於系統開發,引擎開發等應用領 域,支持類、封裝、繼承、多態等特性。
電腦誕生初期,人們要使用電腦必須用機器語言或彙編語言編寫程式。世界上第一種電腦高級語言是誕生於1954年的FORTRAN語言。之後出現了多種電腦高級語言,其中使用最廣泛、影響最大的當推BASIC語言和C語言。BASIC語言是1964年由 Dartmouth 學院 John G. Kemeny 與 Thomas E. Kurtz 兩位教授在FORTRAN語言的基礎上簡化而成的,適用於初學者設計的小型高級語言;C語言是1972年由美國貝爾實驗室的D.M.Ritchie所開發,採用結構化編程方法,遵從自頂向下的原則。在操作系統和系統使用程式以及需要對硬體進行操作的場合,用C語言明顯優於其它高級語言,但在編寫大型程式時,C語言仍面臨著挑戰。
1983年,貝爾實驗室的Bjarne Stroustrup在C語言基礎上推出了C++。C++進一步擴充和完善了C語言,是一種面向對象的程式設計語言。
C++這個名字是Rick Mascitti於1983年中所建議的,並於1983年12月首次使用。更早以前,尚在研究階段的發展中語言曾被稱為“new C”,之後是“C with Class”。在電腦科學中,C++仍被稱為C語言的上層結構。它最後得名於C語言中的“++”操作符(其對變數的值進行遞增)。而且在共同的命名約定中,使用“+”以表示增強的程式。Stroustrup說:“這個名字象徵著源自於C語言變化的自然演進”。
Rick Mascitti在1992年被非正式地問起名字的由來,他表示這是在半開玩笑中說出的。他從沒想過C++會成為這門語言的正式名字。有一個關於C++名字的笑話,當你使用尾碼++時,賦值發生在加運算之前(因此,它應該是++C,而不是C++,這個笑話是說時下某些程式員還在以使用C的方式使用C++,這通常被一些權威著作認為是不正確的)。
在“C with Class”階段,研製者在C語言的基礎上加進去的特征主要有:類及派生類、共有和私有成員的區分、類的構造函數和析構函數、友元、內聯函數、賦值運算符的重載等。
1985年公佈的C++語言1.0版的內容中又添加了一些重要特征:虛函數的概念、函數和運算符的重載、引用、常量(constant)等。
1989年推出的2.0版形成了更加完善的支持面向對象程式設計的C++語言,新增加的內容包括:類的保護成員、多重繼承、對象的初始化與賦值的遞歸機制、抽象類、靜態成員函數、const成員函數等。
1993年的C++語言3.0版本是C++語言的進一步完善,其中最重要的新特征是模板(template),此外解決了多重繼承產生的二義性問題和相應的構造函數與析構函數的處理等。
1998年C++標準(ISO/IEC14882 Standard for the C++ Programming Language)得到了國際標準化組織(ISO)和美國標準化協會(ANSI)的批准,標準C++語言及其標準庫更體現了C++語言設計的初衷。名字空間的概念、標準模板庫(STL)中增加的標準容器類、通用演算法類和字元串類型等使得C++語言更為實用。此後C++是具有國際標準的編程語言,該標準通常簡稱ANSI C++或ISO C++ 98標準,以後每5年視實際需要更新一次標準。
後來又在2003年通過了C++標準第二版(ISO/IEC 14882:2003):這個新版本是一次技術性修訂,對第一版進行了整理——修訂錯誤、減少多義性等,但沒有改變語言特性。這個版本常被稱為C++03。
此後,新的標準草案叫做C++ 0x。對於C++ 0x標準草案的最終國際投票已於2011年8月10日結束,並且所有國家都投出了贊成票,C++0x已經毫無異議地成為正式國際標準。先前被臨時命名為C++0x的新標準正式定名為ISO/IEC 14882:2011,簡稱ISO C++ 11標準。C++ 11標准將取代現行的C++標準C++98和C++03。國際標準化組織於2011年9月1日出版發佈《ISO/IEC 14882:2011》,名稱是:Information technology -- Programming languages -- C++ Edition: 3。
C語言是C++的基礎,C++和C語言在很多方面是兼容的。
C語言是一個結構化語言,它的重點在於演算法與數據結構。C程式的設計首要考慮的是如何通過一個過程,對輸入(或環境條件)進行運算處理得到輸出(或實現過程(事物)控制)。C++,首要考慮的是如何構造一個對象模型,讓這個模型能夠契合與之對應的問題域,這樣就可以通過獲取對象的狀態信息得到輸出或實現過程(事物)控制。所以C語言和C++的最大區別在於它們解決問題的思想方法不一樣。
C++對C的“增強”,表現在六個方面:
(1) 類型檢查更為嚴格。
(2) 增加了面向對象的機制。
(3) 增加了泛型編程的機制(Template)。
(4) 增加了異常處理。
(5) 增加了運算符重載。
(6) 增加了標準模板庫(STL)。
C++一般被認為是C的超集合(Superset),但這並不嚴謹。大部分的C代碼可以很輕易的在C++中正確編譯,但仍有少數差異,導致某些有效的C代碼在C++中失效,或者在C++中有不同的行為。
最常見的差異之一是,C允許從void*隱式轉換到其它的指針類型,但C++不允許。
另一個常見的可移植問題是,C++定義了新關鍵字,例如如new,class,它們在C程式中可以作為識別字(例:變數名)的。
在C標準(C99)中去除了一些不兼容之處,也支持了一些C++的特性,如//註解,以及在代碼中混合聲明。不過C99也納入幾個和C++衝突的新特性(如:可變長度數組、原生複數類型和複合逐字常數)。
若要混用C和C++的代碼,則所有在C++中調用的C代碼,必須放在 extern "C" { /* C代碼 */ } 內。
集成開發環境(IDE)
1.Microsoft Visual Studio(Visual C++)
2.Borland C++ Builder
3.Eclipse(Myln + CDT + MinGW32 + GCC)
4.Dev-C++(MinGW32 + GCC)
5.Code::Blocks(可配合多款編譯器使用)
6.CodeLite
7.C-Free
8.Qt Creator(可配合多款編譯器使用)
9.KDevelop
實際上,當前流行的編譯器只有五種:Intel C++ 編譯器、微軟的cl.exe編譯器(捆綁於Visual Studio套裝中)、GNU的GCC編譯器、LLVM的Clang編譯器、Borland公司的bcc.exe編譯器(捆綁於Borland C++ Builder套裝中)。
- C++設計成靜態類型、和C同樣高效且可移植的多用途程式設計語言。
- C++設計直接的和廣泛的支持多種程式設計風格(程式化程式設計、資料抽象化、面向對象程式設計、泛型程式設計)。
- C++設計無需複雜的程式設計環境。
C++語言靈活,運算符的數據結構豐富、具有結構化控制語句、程式執行效率高,而且同時具有高級語言與彙編語言的優點,與其它語言相比 ,可以直接訪問物理地址,與彙編語言相比又具有良好的可讀性和可移植性。
總得來說,C++語言的主要特點表現在兩個方面,一是儘量兼容C,二是支持面向對象的方法。它操持了C的簡潔、高效的接近彙編語言等特點,對C的類型系統進行了改革的擴充,因此C++比C更安全,C++的編譯系統能檢查出更多的類型錯誤。另外,由於C語言的廣泛使用,因而極大的促進了C++的普及和推廣。
C++語言最有意義的方面是支持面向對象的特征。雖然與C的兼容使得C++具有雙重特點,但他在概念上完全與C不同,更具面向對象的特征。
出於保證語言的簡潔和運行高效等方面的考慮,C++的很多特性都是以庫(如STL)或其他的形式提供的,而沒有直接添加到語言本身里。關於此類話題,Bjarne Stroustrup的《C++語言的設計和演化》(1994)里做了詳盡的陳述。
C++引入了面向對象的概念,使得開發人機交互類型的應用程式更為簡單、快捷。很多優秀的程式框架包括Boost、Qt、MFC、OWL、wxWidgets、WTL就是使用的C++。
- C++由於語言本身複雜。相比於其它編譯型語言,編譯器較難編寫。
- C++豐富的語法特性使得它的學習入門門檻相比於很多其它腳本語言要高,但C++常用特性並不多,可以有選擇性的學習與運用。
數據是程式處理的對象,數據可以依其本身的特點進行分類。我們知道在數學中有整數、實數的概念,在日常生活中需要用字元串來表示人的姓名和地址,有些問題的回答只能是“是”或“否”(即邏輯“真”或“假”)。不同類型的數據有不同的處理方法,例如:整數和實數可以參加算術運算,但實數的表示又不同於整數,要保留一定的小數位;字元串可以拼接;邏輯數據可以參加“與”、“或”、“非”等邏輯運算。
我們編寫電腦程式,目的就是為瞭解決客觀世界中的現實問題。所以,高級語言中也為我們提供了豐富的數據類型和運算。C++中的數據類型分為基本類型和自定義類型。基本類型是C++編譯系統內置的。
C++的基本數據類型如下表所示(下表中列出的各類型的長度和取值範圍,是指以面向80x86處理器的 Viusal C++ 2012 和 gcc 4.8.1 的長度,其它的編譯器並不一定是這個長度)。
- bool所支持的位元組數是1b,取值範圍是false、true;
- char、signed char所支持的位元組數是1b,取值範圍是-128~127;
- unsign char所支持的位元組數是1b,取值範圍是0~255;
- short (signed short)所支持的位元組數是2byte,取值範圍是-32768~32767;
- unsigned short所支持的位元組數是2byte,取值範圍是0~65535;
- int (signed int)所支持的位元組數是4b,取值範圍是-2147483648~2147483647;
- unsigned int所支持的位元組數是4b,取值範圍是0~4294967295;
- long (signed long)所支持的位元組數是4b,取值範圍是-2147483648~2147483647
- unsigned long所支持的位元組數是4b,取值範圍是0~4294967295
- unsigned long long所支持的位元組數是8b,取值範圍是0~18446744073709551615
- float所支持的位元組數是4b,取值範圍是
- double所支持的位元組數是8b,取值範圍是
- long double所支持的位元組數是8b,取值範圍是
類型轉換
C++豐富的數據類型允許根據需要選擇不同的類型,這也使得電腦的操作更複雜。
自動類型轉換(隱式類型轉換)
在下列四種情況中電腦自動進行類型轉換:
(1)在混合類型的算術表達式中
intival=3; doubledval=3.1415 ival+dval;//ival被提升為double類型:3.0
(2)用另外一種類型的表達式賦值
int*pi=NULL;//NULL(0)被轉換成了int*類型的空指針值
(3)用一個表達式傳遞給一個函數調用
externdoublesqrt(double); sqrt(2);//2被提升為double類型:2.0
(4)從一個函數返回一個表達式
doubledifference(intival1,intival2) { returnival1-ival2;//返回值被提升為double類型. }
(一)new和delete
運算符new和delete提供了存儲的動態記憶體分配和釋放功能,它的作用相當於C語言的函數malloc()和free(),但性能更為優越。使用new較之使用malloc()有以下的幾個優點:
(1)new自動計算要分配類型的大小,不使用sizeof運算符,比較省事,可以避免錯誤。
(2)自動地返回正確的指針類型,不用進行強制指針類型轉換。
(3)可以用new對分配的對象進行初始化。
(二)inline
對於頻繁使用的函數,C語言建議使用巨集調用代替函數調用以加快代碼執行,減少調用開銷。但是巨集調用有許多的弊端,可能引起不期望的副作用。例如巨集:#define abs(a)(a)<0?(-a):(a)),當使用abs(i++)時,這個巨集就會出錯。
所以在C++中應該使用inline內聯函數替代巨集調用,這樣既可達到巨集調用的目的,又避免了巨集調用的弊端。
使用內聯函數只須把inline關鍵字放在函數返回類型的前面。
(三)函數重載
在C語言中,兩個函數的名稱不能相同,否則會導致編譯錯誤。而在C++中,函數名相同而參數數據類型不同或參數個數不同或二者皆不同的兩個函數被解釋為重載。
使用函數重載可以幫助程式員處理更多的複雜問題,避免了使用諸如intabs()、fabs()、dabs()等繁雜的函數名稱;同時在大型程式中,使函數名易於管理和使用,而不必絞盡腦汁地去處理函數名。同時必須註意,參數數據類型相同,但是函數返回類型不同的兩個函數不能重載。
(四)參數傳遞
在C語言中,如果一個函數需要修改用作參數的變數值的時候 ,參數應該聲明為指針類型;當參數的大小超過一個機器字長時,通過傳值方式來傳遞參數的效率較低,也需要用指針。由於C語言的指針可以進行p++,--p,p+=1等算術運算,所以編譯器無法在編譯的時候確定指針引用的變數。對於複雜的程式,使用指針容易出錯,程式也難以讀懂。在C++中,對於上述情況 可以使用引用來代替指針,使程式更加清晰易懂。引用就是對變數取的一個別名,對引用進行操作,這就相當於對原有變數進行操作。
(五)預設參數
在C++中函數可以使用預設參數。
通常的情況下,一個函數應該具有儘可能大的靈活性。使用預設參數為程式員處理更大的複雜性和靈活性問題提供了有效的方法,所以在C++的代碼中都大量地使用了預設參數。
需要說明的是,所有的預設參數必須出現在不預設參數的右邊。亦即,一旦開始定義預設參數,就不可再聲明非預設的參數。否則當你省略其中一個參數的時候,編譯器無法知道你是自定義了這個參數還是利用了預設參數而定義了非預設的參數。
(六)使用STL
STL(Standard Template Library,標準模板庫),STL的代碼從廣義上講分為三類:algorithm(演算法)、container(容器)和iterator(迭代器),並包括一些工具類如auto_ptr。幾乎所有的代碼都採用了模板類和模板函數的方式,這相比於傳統的由函數和類組成的庫來說提供了更好的代碼重用機會。
作用符被重載,使得我們可以像訪問數組一樣訪問vector中的元素。
(七)使用模板
①模板的概念
模板是C++的一個特性,是函數和類可以作用於不同的類型上而不需要針對每一個具體類型重覆相同的代碼。與模板相反,我們已經學過的重載(Overloading),對重載函數而言,C++的檢查機制能通過函數參數的不同及所屬類的不同。正確的調用重載函數。例如,為求兩個數的最大值,我們定義MAX()函數需要對不同的數據類型分別定義不同重載(Overload)版本。如果使用模板就可以只寫一個通用的MAX模板,而不需要針對每個類型重覆相同的邏輯。
②指針與引用的區別
指針與引用看上去完全不同(指針用操作符“*”和“->”,引用使用操作符“&”),但是它們似乎有相同的功能。指針與引用都是讓你間接引用其他對象。你如何決定在什麼時候使用指針,在什麼時候使用引用呢?
首先,要認識到在任何情況下都不能使用指向空值的引用。一個引用必須總是指向某些對象。因此如果你使用一個變數並讓它指向一個對象,但是該變數在某些時候也可能不指向任何對象,這時你應該把變數聲明為指針,因為這樣你可以賦空值給該變數。相反,如果變數肯定指向一個對象,例如你的設計不允許變數為空,這時你就可以把變數聲明為引用。
類是具有相同屬性和相同的方法的對象的集合,它是一種既包含數據又包含函數的抽象數據類型。
對象是類進行實體化後的產物,是一個實體。
在C++中也是先聲明一個類類型,然後用它去定義若幹個同類型的對象。對象就是類類型的一個變數。可以說類是對象的模板,是用來定義對象的一種抽象類型。類是抽象的,不占用記憶體,而對象是具體的,占用存儲空間。
類是用戶自己指定的類型。如果程式中要用到類類型,必須自己根據需要進行聲明,或者使用別人已設計好的類。C++標準本身並不提供現成的類的名稱、結構和內容。
在C++中聲明一個類的類型和聲明一個結構體類型是相似的。
在聲明類類型時,聲明為private的成員和聲明為public的成員的次序任意,既可以先出現private部分,也可以先出現public部分。
在一個類體中,關鍵字private和public可以分別出現多次。每個部分的有效範圍到出現另一個訪問限定符或類體結束時(最後一個右花括弧)為止。但是為了使程式清晰,應該養成這樣的習慣: 使每一種成員訪問限定符在類定義體中只出現一次。
C++ 程式多數先寫public部分,把private部分放在類體的後部。這樣可以使用戶將註意力集中在能被外界調用的成員上,使閱讀者的思路更清晰一些。
在C++程式中,經常可以看到類。為了用戶方便,常用的C++編譯系統往往向用戶提供類庫(但不屬於C++語言的組成部分),內裝常用的基本的類,供用戶使用。不少用戶也把自己或本單位經常用到的類放在一個專門的類庫中,需要用時直接調用,這樣就減少了程式設計的工作量。
上述程式段中,最後一行用已聲明的Student類來定義對象,這種方法是很容易理解的。經過定義後,stud1和stud2就成為具有Student類特征的對象。stud1和stud2這兩個對象都分別包括Student類中定義的數據和函數。
定義對象也可以有以下幾種方法:
1.先聲明類類型,然後再定義對象前面用的就是這種方法,如Student stud1,stud2;//Student是已經聲明的類類型在C++中,聲明瞭類類型後,定義對象有兩種形式。
(1)class 類名 對象名
如: class Student stud1,stud2;
把class和Student合起來作為一個類名,用來定義對象。
(2)類名 對象名
如: Student stud1,stud2;
直接用類名定義對象。
這兩種方法是等效的。第1種方法是從C語言繼承下來的,第2種方法是C++的特色,顯然第2種方法更為簡捷方便。
2.在聲明類類型的同時定義對象
3.不出現類名,直接定義對象
直接定義對象,在C++中是合法的、允許的,但卻很少用,也不提倡用。在實際的程式開發中,一般都採用上面3種方法中的第1種方法。在小型程式中或所聲明的類只用於本程式時,也可以用第2種方法。在定義一個對象時,編譯系統會為這個對象分配存儲空間,以存放對象中的成員。
C++增加了class類型後,仍保留了結構體類型(struct ),而且把它的功能也擴展了。C++允許用struct來定義一個類型。如可以將前面用關鍵字class聲明的類類型改為用關鍵字struct為了使結構體類型也具有封裝的特征,C++不是簡單地繼承C的結構體,而是使它也具有類的特點,以便於用於面向對象程式設計。用struct聲明的結構體類型實際上也就是類。用struct聲明的類,如果對其成員不作private或public的聲明,系統將其預設為public。
如果想分別指定私有成員和公用成員,則應用private或public作顯式聲明。
而用class定義的類,如果不作private或public聲明,系統將其成員預設為private,在需要時也可以自己用顯式聲明改變。如果希望成員是公用的,使用struct比較方便,如果希望部分成員是私有的,宜用class。建議儘量使用class來建立類,寫出完全體現C++風格的程式。
關鍵字(keyword)是整個語言範圍內預先保留的標識符。每個C++關鍵字都有特殊的含義。經過預處理後,關鍵字從預處理記號(preprocessing-token)中區出來,剩下的標識符作為記號(token),用於聲明對象、函數、類型、命名空間等。不能聲明與關鍵字同名的標識符。
各個版本的ISO C++都規定以下劃線接大寫字母起始的標識符保留給實現。編譯器可以用這些保留標識符作為擴展關鍵字,這不保證可移植性。以下討論ISO C++所保留的關鍵字。
一個程式從編寫到最後得到運行結果要經歷以下一些步驟:
1、用C++語言編寫程式
用高級語言編寫的程式稱為“源程式”(source program)?源程式的尾碼取決於C++實現。
2、對源程式進行編譯
為了使電腦能執行高級語言源程式,必須先用一種稱為“編譯器(complier)”的軟體(也稱編譯程式或編譯系統),把源程式翻譯成二進位形式的“目標程式(object program)?
編譯是以源程式文件為單位分別編譯的?目標程式一般以.obj或.o作為尾碼(object 的縮寫)?編譯的作用是對源程式進行詞法檢查和語法檢查?編譯時對文件中的全部內容進行檢查,編譯結束後會顯示出所有的編譯出錯信息?一般編譯系統給出的出錯信息分為兩種,一種是錯誤(error);一種是警告(warning) ?
3、將目標文件連接
在改正所有的錯誤並全部通過編譯後,得到一個或多個目標文件?此時要用系統提供的“連接程式(linker)”將一個程式的所有目標程式和系統的庫文件以及系統提供的其他信息連接起來,最終形成一個可執行的二進位文件,它的尾碼是.exe,是可以直接執行的?
4、運行程式
運行最終形成的可執行的二進位文件(.exe文件),得到運行結果?
如果運行結果不正確,應檢查程式或演算法是否有問題?
在Unix世界有大量的程式員是傳統的非IDE的方式進行軟體開發。一般是如下組合:
1.編譯器:gcc和clang等。
2.編輯器:常用Vim和Emacs
3.make:GNU make 或者BSD的pmake等,功能與用法基本一樣
4.版本管理:cvs,svn,git等等
5.代碼閱讀:cscope,ctags,lxr等
根據Effective C++第三版第一條款的描述,C++由以下四個“子語言”組成:
1、C子語言。C++支持C語言的幾乎全部功能,主要是c89的部分,在語法上與C語言僅有極微妙的差別(如括弧表達式的左右值性,具體請參考C++標準文獻)。這部分功能對應於傳統的面向過程的編程泛型,並提供了面向函數編程泛型的基礎。
2、面向對象的C++語言。C++語言原本不具備面向對象的設計功能,然而隨著面向對象編程的概念的普及,C++語言也開發出了支持面向對象功能的版本。這部分功能對應於面向對象的編程泛型。
3、泛型編程語言。C++強大(但容易失控的)模板功能使它能在編譯期完成許多工作,從而大大提高運行期效率,並且大大提高了C++的表達能力。STL(C++標準模板庫,Standard Template Library)是一個基於模板技術的庫。隨著STL的不斷發展,它已經逐漸成為C++程式設計中不可或缺的部分,其效率可能比一般的native代碼低些,但是其安全性與規範性使它大受歡迎。模板使C++能夠支持泛型編程(generic programming)和生成式編程(generative programming)的泛型。
4、在C++11中引入的Lambda,使得程式員可以定義匿名函數,完善了C++對於面向函數的編程泛型的支持。
C++ 98 標準
C++標準第一版,1998年發佈。正式名稱為ISO/IEC 14882:1998。
絕大多數編譯器都支持C++98標準。不過當時錯誤地引入了export關鍵字。由於技術上的實現難度,除了Comeau C++編譯器export關鍵字以外,沒有任何編譯器支持export關鍵字。並且這個標準對現代的一些編譯理念有相當的差距,有很多在高級語言都應當有的功能,它都沒有。這也正是後來需要制定C++11標準的原因所在。
C++ 03 標準
C++標準第二版,2003年發佈。正式名稱為ISO/IEC 14882:2003。這個標準僅僅是C++98修訂版,與C++98幾乎一樣,沒做什麼修改。僅僅是對C++98做了一些“勘誤”,就連主流編譯器(受C99標準影響)都已支持的long long都沒有被加入C++03標準。
C++ 11 標準
C++標準第三版,2011年8月12日發佈。正式名稱為ISO/IEC 14882:2011。
由C++標準委員會於2011年8月12日公佈,並於2011年9月出版。2012年2月28日的國際標準草案(N3376)是最接近於現行標準的草案(編輯上的修正)。C++11包含了核心語言的新機能,並且拓展C++標準程式庫,並且加入了大部分的C++ Technical Report 1程式庫(數學上的特殊函數除外)。此次標準為C++98發佈後13年來第一次重大修正。
註意: C++11標準(ISO/IEC 14882:2011)與C11標準(ISO/IEC 9899:2011)是兩個完全不同的標準,後者是C語言的標準。
C++ 14 標準
C++標準第四版,2014年8月18日發佈。正式名稱為ISO/IEC 14882:2014。
2014年8月18日,ISO組織在其網站上發佈文章稱:
C++ 作者 Bjarne Stroustrup 稱,主要的編譯器開發商已經實現了 C++ 14 規格。
C++ 14 是 C++ 11 的增量更新,主要是支持普通函數的返回類型推演,泛型 lambda,擴展的 lambda 捕獲,對 constexpr 函數限制的修訂,constexpr變數模板化等等。
C++14是C++語言的最新標準,正式名稱為"International Standard ISO/IEC 14882:2014(E) Programming Language C++"。C++14旨在作為C++11的一個小擴展,主要提供漏洞修複和小的改進。C++14標準的委員會草案(Committee Draft)N3690於2013年5月15日發表[18] 。工作草案(Working Draft)N3936已於2014年3月02日完成。最終的投票期結束於2014年8月15日,結果(一致通過)已於8月18日公佈。