編譯期常量折疊
Ⅰ C++中常量賦值是如何規定的
1、值替代
C語言中預處理器用值代替名字:
#define BUFSIZE 100
這種方式在C++中依然適用,同時
C++用const把值代替帶進編譯器領域:
const bufsize = 100 ;或 const int bufsize = 100 ;
同時還可以執行常量折疊:
char buf[bufsize] ;
1.1 頭文件里的const
與使用#define一樣,使用const必須把const定義放在頭文件里。這樣,通過包含頭文件,可把const定義單獨放在一個地方,並把她分配給一個編譯單元。const默認為內部連接,即僅在const被定義過的文件里才是可見的,而在連接時不能被其他編譯單元看到。
比如說在Test.h中定義了,
const int nTestConst = 1000;
在A.cpp中include"Test.h"
同時在B.cpp中include"Test.h"
不會出現,當沒有定義為const時,因變數定義出現兩次而出錯的情形.
當定義一個const常量時,必須賦一個值給它,除非用extern作了清楚的說明:
extern const bufsize ;
雖然上面的extern強制進行了存儲空間分配,但C++編譯器並不為const分配存儲空間,相反它把這個定義保存在它的符號表裡。當const被使用時,它在編譯時會進行常量折疊。
1.2 const的安全性
如果想要一個值保持不變,就應該使它成為一個常量(const)。
1.3 集合
const可以用於集合,但必須分配內存,意味著「不能改變的一塊存儲」。但其值在編譯時不能被使用。
const int i [] = {1,2,3,4};
// float f [ i [3] ]; // Illegal
2,指針
當使用帶有指針的const時,有兩種選擇:1、或者const修飾指針正指向的對象,2、const修飾存儲在指針本身的地址。
2.1 指向const的指針
使用指針定義的技巧,正如任何復雜的定義一樣,是在標識符的開始處讀它並從里到外讀。如果我們要使正指向的元素不發生改變,得寫:
const int * x;
從標識符開始:「x 是一個指針,指向一個const int。」
把const從一邊移到另一邊,結果一樣。
int const * x;
2.2 cosnt指針
是指針本身成為一個const指針,必須把const標明的部分放在*的右邊,如:
int d = 1;
int * const x = &d;
指針本身是const指針,編譯器要求給它一個初始化值,這個值在指針壽命期間不變。然而要改變它所指向的值是可以的,可以寫*x = 2。
這些可以總結成這樣一句話:
以*為分界點,
當const在*的左邊時,實際物體值不變
當const在*的右邊時,指針不變,
即,左物右指
這樣來記比較方便!!
Ⅱ java中編譯期常量所指的是什麼
classInitalizedClass{
static{
System.out.println("!");
}
publicstaticintinititalize_varible=1;
}
{
publicstaticvoidmain(String[]args){
System.out.println(InitalizedClass.inititalize_varible);
}
}
上面的結果是:
!
1
classInitalizedClass{
static{
System.out.println("!");
}
//和上面的例子唯一的差異就是此處的變數INITIALIZED_VARIBLE被聲明為final
_VARIBLE=1;
}
{
publicstaticvoidmain(String[]args){
System.out.println(InitalizedClass.INITIALIZED_VARIBLE);
}
}
上面的結果是:
1
為什麼兩個例子執行結果不一樣,原因是第二個例子中的INITIALIZED_VARIBLE為編譯期常量,它不會導致類的初始化的
Ⅲ String a="a"; String b="b"; a=a+b; 這里共創建了幾個對象
這里共創建了3個對象。
"a"+"b"+"c"在編譯期已經常量折疊為"abc",變數a獲得是"abc"。
甲骨文jdk(1.7),javac會進行常量折疊,全字面量字元串相加是可以折疊為一個字面常量,而且是進入常量池的。這個問題涉及到了字元串常量池和字元串拼接。
只創建了一個對象,在字元串池只會有一個對象。因為它是一行定義的對象,編譯時只會初始化一次字元串緩沖池的數據。如果是 String a="a";String b="b";String c="c";String d=a+b+c;這里就創建了4個對象。
(3)編譯期常量折疊擴展閱讀
String 對象的實現:
String 對象的創建方式
1、通過字元串常量的方式
String str= "pingtouge"的形式,使用這種形式創建字元串時, JVM 會在字元串常量池中先檢查是否存在該對象,如果存在,返回該對象的引用地址,如果不存在,則在字元串常量池中創建該字元串對象並且返回引用。
使用這種方式創建的好處是:避免了相同值的字元串重復創建,節約了內存
2、String()構造函數的方式
String str = new String("pingtouge")的形式,使用這種方式創建字元串對象過程就比較復雜,分成兩個階段,首先在編譯時,字元串pingtouge會被加入到常量結構中,類載入時候就會在常量池中創建該字元串。
然後就是在調用new()時,JVM 將會調用String的構造函數,同時引用常量池中的pingtouge字元串,在堆內存中創建一個String對象並且返回堆中的引用地址。
Ⅳ 在學習到const_cast時引發的疑惑,尋求協助,謝謝~
//問題一:不會,a仍然是常量,任何轉換都不能將被轉換對象轉換成別的形式,而只能是使用轉換後的結果。
//問題二:對於C++,簡單類型的常量會視情況給予分配內存空間或不分配而直接按符號常量來處理,當被定義的常量後面有取該常量地址的操作時(即使用&運算符),它會被分配內存空間而等同與C語言中的常量,當沒有對其進行取地址運算時,它就等同於#define定義的宏,即符號常量。第6行中的arr也存在相同的情形,即可能被分配空間也可能被當成符號常量,比如arr[2]可能被實現成*(arr+2)也可能直接將arr[2]當成一個符號常量,這要看編譯器是否進行了深度優化。
//問題三:定義在全局數據區中的標識符,其中的static只有一個作用:即禁止使用extern關鍵字聲明它以在別的文件中使用該標識符,基於此,value與普通常量沒什麼差別,只是無法在別的文件中使用value而已。
//問題四:在我的VC6編譯器中,即使移除static也會出現內存非法操作錯誤,這個問題是C++標准中未明確規定所導致的,C++並沒有規定常量必須放到哪種類型的內存中,但編譯器經常會將常量放到只讀內存中,而只讀內存是不允許寫操作的,因此第四行會出現內存非法操作的運行時錯誤。
//問題五:value何時被重新分配內存了?對value取地址取的就是定義value時的地址。
//問題六:在你的這段代碼中,很明顯value被編譯器實現成了符號常量,因此*pa=10修改的是另一個「克隆」內存區,而你輸出value時等價於直接輸出5,而常量數組arr則被實現成非符號常量即真正的有內存地址的常量,因此可以修改其中的值。
Ⅳ define定義的符號常量和用const定義的變數有什麼不同為什麼不用const修飾常量呢
define 預編譯期的。
const 是編譯期的。
define 定義值一個符號 沒有類型之分的
const 編譯指定了類型。
為什麼不用const修飾常量呢 。 const 修飾的表示一個常量。內部類型會常量折疊的,不會分配地址空間
Ⅵ C++語法高手進
C 基本語法(上)
0. 編譯C 程序時,編譯器自動定義了一個預處理名字__cplusplus,而編譯標准C時,自動定義名字__STDC__。另外幾個比較有用的預定義名字是__LINE__(文件的當前編譯行數),__FILE__(當前被編譯的文件名字),__DATE__(編譯日期)和__TIME__(編譯時間)。
1. C庫頭文件的C 名字總是以字母C開頭,後面去掉後綴.h的C名字,如在C 庫中的名字是。兩種使用方法:
#include 或者
#include
using namespace std;
2. 靜態與動態內存分配的兩個主要區別:(1)靜態對象是有名字的變數,可以直接進行操作,動態對象是沒有名字的變數,可以通過指針間接進行操作;(2)靜態對象的分配和釋放由編譯器自動處理,而動態對象必須由程序員顯式的管理,通過new和delete兩個表達式來完成。
3. 類的預設構造函數是不需要用戶提供任何參數的構造函數。
4. STL中標准數組-vector(文件),兩種遍歷方式:(1)通過下標操作符;(2)使用迭代器,如vector<int>::iterator iter;可以通過對iterator解引用來直接訪問實際的元素*iter;STL提供了作用於容器類型的泛型演算法,頭文件。
5. 文字常量是不可定址的。
6. 常用的幾個轉義序列:
newline(換行符) \n \14
horizontal tab(水平製表符) \t
vertical tab(垂直製表符) \v
backspace(退格符) \b
carriage return(回車鍵) \r
formfeed(進紙鍵) \f
alert (bell)(響鈴符) \a \7
7. 變數和文字常量都有存儲區,區別在於變數是可定址的,對於每個變數,都有兩個值與之關聯:數據值,稱為對象的右值,是被讀取的值,文字常量和變數都可以用作右值;地址值,被稱為變數的左值,是位置值,文字變數不用被用作左值。
8. 每個指針都有一個相關的類型。不同數據類型的指針之間的區別在於指針所指的對象的類型上。如果我們需要的僅僅是持有地址值,C 提供了一種特殊的指針類型:空(void *)類型指針,它可以被任何數據指針類型的地址值賦值,除了函數指針。不能操作空類型指針所指向的對象,只能傳送該地址值或將它與其他地址值做比較。
9. C風格的演算法循環:
while(*p ){…}
10. 正確定義引用,如下:
const int ival = 1024;
const int *&pi_ref = &ival; // 錯誤,pi_ref是一個引用,它指向定義為const的int型對象的一個指針,引用不是指向常量,而是指向了一個非常量指針
const int *const &pi_ref = &ival; // OK
11. 指針和引用有兩個主要區別,引用必須總是指向一個變數;如果一個引用給另外一個引用賦值,那麼改變的是被引用對象而不是引用本身。
12. 布爾類型對象雖然也被看作整數類型的對象,但是它不能被聲明為signed,unsigned,short或long。
13. 一個數組不能被另外一個數組初始化,也不能被賦值給另外一個數組,而且,C 不容許聲明一個引用數組。
14. 數組標志符代表數組中的第一個元素的地址。它的類型是數組元素類型的指針。
int ia[10];
第一個元素的地址: ia或者是&ia[0]
第二個元素的地址: ia 1或者是&ia[1]
15. STL中vector有兩種不同的使用形式:數組習慣,即使用下標操作符,注意只能操作已經存在的成員;STL習慣,使用iterator來操作,對其解引用可以訪問實際的對象,也可以通過加減來移動位置。雷同與5。
16. typedef用來為內置的或用戶定義的數據類型引入助記符號。
typedef char *cstring;
extern const cstring cstr;
其中cstr的類型是 char *const cstr;
17. 當一個對象的值可能會在編譯器的控制或監制之外被改變時,那麼該變數應該聲明為volatile,編譯器執行的某些例行優化不能應用在已經指定為volatile的對象上。
18. pair類可以在單個對象內部把相同類型或不同類型的兩個值關聯起來。我們可以使用成員訪問符號來訪問pair中的單個元素,他們的名字為first和second。
19. 在類體外定義的內聯成員函數,應該被包含在含有該類定義的頭文件中。
20. setw()是一個預定義的iostream操作符,它讀入的字元數最多為傳遞給它的參數減一。如setw(1024),則最多讀入1023個字元。
21. 標准C 頭文件提供了與內置類型表示有關的信息,另外,還有標准C頭文件和。
22. 對於二元操作符<或者>,左右操作數的計算順序在標准C和C 中是都是未定義的,因此計算過程必須是與順序無關的。如ia[index ]就是未定義的。
23. 初始化過程為對象提供初值,而賦值是用一個新值覆蓋對象的當前值,一個對象只能被初始化一次,也就是在它被定義的時候,而賦值可以多次。如初始化int ival = 1024;賦值 ival = 1025;賦值操作符的左操作數必須是左值。
24. sizeof操作符的作用是返回一個對象或者類型名的位元組長度,返回值類型是size_t,這是一種與機器有關的typedef定義,可以在文件中找到它的定義。
25. 按位非操作符(~)翻轉操作數的每一位。移位操作符(<<和>>)將其左邊操作數的位向左或者向右移動某些位,移到外面的位被丟棄,左移操作符從右邊開始用0填充空位。右移操作符,如果是無符號數從左邊開始插入0,否則它或者插入符號位的拷貝或者插入0,這由具體實現定義。按位與(&)對兩個操作數的每一位進行與操作(只有兩位同時為1時值才為1)。按位異或(^)操作符對兩個操作數的每一位進行異或操作(只有兩個含有一個1時值才為1,即兩位不同值為1)。按位或(|)操作符對兩個操作數的每一為進行或操作(只有兩位同時為0時值才為0)。如將整數a的第27位設為1:a |= 1 << 27;將第27為設為0:a &= ~(1 << 27) ;測試第27位是否為1:a & (1 << 27)。
26. bitset類,頭文件為,支持三種構造方式,第一是直接指定向量長度,如bitset <32> bs;第二是顯式提供一個無符號參數,如bitset<32> bs(012);將第1和第3位設置為1。第三是傳遞一個代表1和0集合的字元串參數,還可以標記字元串的范圍,如string bitval(「1111110101100011010101」);bitset<32> bs(bitval, 6, 4);則bs的第1和第5位被初始化為1;如果去掉指定字元串范圍的第三個參數,則范圍是指定的位置開始一直到字元串的末尾。而函數to_string和to_ulong則把bitset對象轉換為字元串和整型表示。
27. 操作符優先順序表
28. 隱式轉換發生在下列情況下:1.混合類型的算術表達式,即算術轉換;2.用一種類型的表達式賦值給另一種類型的對象;3.把一個表達式傳遞給一個函數調用,表達式的類型和形式參數的類型不相同;4.從一個函數返回一個表達式。
29. 算術轉換的兩個原則:1.如果必要的話,類型總是提升為較寬的類型;2.所有含有小於整型的整值類型的算術表達式,在計算之前,其類型都會被轉換為整型。
30. const_cast轉換掉表達式的常量性以及volatile對象的volatile性;如const_cast<char *>ptr;編譯器隱式執行的任何類型轉換都可以由static_cast顯式完成。reinterpret_cast通常對操作數的位模式執行一個比較低層次的重新解釋。dynamic_cast支持在運行時刻識別由指針或引用指向的類對象。
31. typename是標准C 中新引入的關鍵字,用於template中。
32. 兩個主要的順序容器是list和vector,另外一個順序容器是deque;兩個主要的關聯容器是map和set。
33. 選擇順序容器類型的一些准則:(1)隨機訪問,vector比list好得多;(2)已知道元素的個數,則vector比list強;(3)如果不只是在容器兩端插入和刪除元素,則list比vector強很多;(4)除非需要在容器首部插入和刪除元素,則vector比deque好。對於小的數據類型,vector的插入大批量數據的性能比list好很多,而大型數據時則相反,解決方案是只保留大型數據的指針。Reserve函數容許將容器的容量設置成一個顯式指定的值,resize函數重新設置容器的長度;每個容器支持一組關系操作符;用戶定義的類類型必須支持等於操作符,必須支持小於操作符,元素類型必須支持一個預設值(對類類型,指預設構造函數)。
34. 除了iterator類型,每個容器還定義了一個const_iterator類型,對遍歷const容器是必需的,const_iterator容許以只讀方式訪問容器的底層元素。Iterator算術運算(是指加法或者減法等算術運算,而不是重載的 操作符)只適用vector或deque,而不適用於list因為list在內存中不是連續存儲的。
35. string類的find函數返回一個索引類型string::size_type或者返回string::npos;find_first_of提供了被搜索字元串中任意一個字元相匹配的第一次出現,並返回它的索引位置,substr函數生成現有string對象的子串的一個拷貝,第一個參數指明開始的位置,第二個可選的參數指明子串的長度。rfind,查找最後的指定子串的出現;find_first_not_of查找第一個不與要搜索字元串的任意字元相匹配的字元;find_last_of查找與搜索字元串任意元素相匹配的最後一個字元;find_last_not_of查找字元串中的與搜索字元串任意字元全不匹配的最後一個字元。
36. tolower函數接受一個大寫字元,並返回與之等價的小寫字母,必須包含頭文件,另外一些該文件中的函數,isalpha, isdigit, ispunct, isspace, toupper等。
37. assign對把字元串進行賦值;append則類似於 =操作符;swap交換兩個string的值;進行越界檢查的at類似於[]操作符;compare函數提供了兩個字元串的字典序比較;replace函數提供了十種方式,可以用一個或者多個字元替換字元串中的一個或多個現有的字元。
38. map定義了一個類型value_type,表示相關的鍵值對,用於insert函數。
39. 關聯容器支持以下兩個函數:find查找指定key,不存在則返回end();count返回指定key值的出現的次數。
40. set定義了一個類型difference_type,是兩個iterator相減的結果類型;inserter類用來執行插入操作。如:(in, eos, inserter(set1, set1.begin());因為執行的是賦值操作,所以使用inserter使用插入操作取代賦值操作。
41. multiset和multimap的特殊操作equal_range返回iterator對值,如:pair pos; pos = mp.equal_range(tst);
42. stack類,頭文件,函數top和pop分別是訪問棧頂元素和刪除棧頂元素。棧類型是容器適配器,因為它把棧抽象施加到底層容器上,預設情況下棧使用deque實現,可以自定義預設的底層容器,如使用list構建stack:stack<int, list<int> > intStack;
43. C 中,數組永遠不會按值傳遞,它總是傳遞第一個元素(准確的說是第0個)的指針。多維數組被傳遞為指向第0個元素的指針。如matrix[][10]等同於int (*matrix)[10],都表示matrix是個二維數組,每行10列。而int *matrix[10]表示一個含有10指向int的指針的數組。
44. UNIX系統函數chmod改變文件的保護級別,它的函數聲明在系統頭文件中。
45. 鏈接指示符的第一種形式由關鍵字extern後跟一個字元串常量以及一個普通的函數聲明構成,如extern 「C」 void exit();第二種形式是是多個函數聲明用大括弧包含再鏈接指示符復合語句中。鏈接指示符不能出現在函數中,而且放在頭文件中更合適。
46. 支持命令行格式的主函數:int main(int argc, char *argv[]);
47. 指向函數的指針,如int (*pFunc)(int, int);將取地址符作用於函數名上也能產生指向該類型的指針。如已經存在函數定義int f1(int); int (*pf1)(int)=f1; int (*pf2)(int)=&f1;函數調用可以使用pf1(1)格式或者(*pf1)(1)格式。當一個函數名沒有被調用操作符修飾時,會被解釋為該類型函數的指針,如存在函數定義int f(int);表達式f被解釋為int (*)(int)。
48. 函數指針數組的定義:int (*testCases[10])();使用typedef可以使聲明更易讀。如typedef int (*PFV)(); PFV testCases[10];聲明一個「指向函數指針數組的指針」,如PFV (*pArray)[10];調用其中函數(*pArray)[2]();
49. 函數參數的類型不能是函數類型,函數類型的參數將被自動轉換為該函數類型的指針。如存在函數類型typedef int funtype(int);void sort(int, funtype);與函數定義sort(int, int(*)(int));等價。函數指針也可以作為函數返回值的類型,如int (*ff(int))(int *, int);該聲明將ff聲明一個函數,它有一個int的參數,它的返回值為一個指向函數的指針,類型為int (*)(int *,int);
50. 指向C函數的指針與指向C 的函數指針類型不同,也就是:int (*fcpp)(int)與extern 「C」 int (*fpc)(int)類型不同。並且,當鏈接指示符作用一個聲明上時,所有被它聲明的函數都受影響,如extern 「C」 void f1(void (*pfParm)(int));其中pfParm也是C函數指針。因此,要實現一個含有C函數指針作為參數的C 函數,可以使用typedef,如extern 「C」 typedef void FC(int); C 函數 void f2(FC *pfParam);
51. 關鍵字extern為聲明但不定義提供了一種方法,extern聲明不會引起內存被分配。
52. 設計頭文件時不應該含有非inline函數和對象的定義。符號常量的定義和inline函數可以被重復定義多次,在編譯期間,在可能的情況下,符號常量的值會取代該名字的出現,這個過程為常量折疊。符號常量是任何const類型的對象。但是如下定義不符合 char *const buf = new char[10];因為它的值不能在編譯時確定,不能定義在頭文件;const char *msg =」Error」;因為msg不是常量,它是指向常量值的非常量指針,必須修改為const char *const msg=」」;
53. 有三種局部對象:自動對象,寄存器對象和局部靜態對象。在函數中頻繁使用的變數可以使用關鍵字register聲明為寄存器對象。
54. delete表達式只能作用於由new表達式從空閑存儲區分配的指針上,如果作用於其他內存指針上,可能導致問題,但是作用於值為0的指針上,delete不會指向任何操作。
55. auto_ptr是STL提供的類模板,可以幫助程序員自動管理用new表達式動態分配的單個對象。頭文件為,只有擁有底層對象所有權的對象負責釋放內存,但使用Copy constructor和Assignment進行操作時,左邊的對象得到所有權,而右邊的則被撤銷所有權。使用get來測試是否指向一個底層對象,使用reset來重新設置一個底層對象,assignment操作符不支持內存指針。
56. 可以創建動態的const對象,如const int *pci = new const int(1024);不能創建const對象數組,創建的const對象必須初始化。
57. 定位new表達式容許程序員要求將對象創建在已經被分配好的內存中。如:char *buf=new char [1024];Foo *pb = new (buf) Foo;
58. 可以使用未命名的namespace來聲明一個局部於某一文件的實體,即該函數只在當前文件中可見。通常在實現文件中使用,這種方法是用來替換C語言中的static函數聲明。
59. 重載函數,兩個函數的函數名相同,但函數參數表必須不同,參數個數或參數類型不同;識別函數聲明並不考慮傳值參數類型是const或者volatile,即void f(int)和函數void f(const int)是同一個函數。但是如果是傳地址或者傳引用時,則需要考慮這兩個修飾符。void f(int *)和函數void f(const int *)為不同的函數。
60. 重載函數集合中的所有函數都應該在同一個域中聲明。using聲明和using指示符可以使得一個名字空間的成員在另外一個名字空間可見,這對重載函數集合產生影響。using聲明總是為重載函數的所有函數聲明別名,如果引入一個函數在該域中已經存在並且參數表相同,則產生錯誤。而extern 「C」鏈接指示符只能指定重載函數集中的一個函數。
61. 函數重載解析的三個步驟(1)確定函數調用考慮的重載函數的集合,確定函數調用中實參表的屬性;(2)從重載函數集合中選擇函數,該函數可以在給定的情況下用實參進行調用;(3)選擇與調用最匹配的函數。在第二步中,編譯器確定需要進行的參數轉換,並劃分等級,通常分為精確匹配;與一個類型轉換匹配;無匹配。其中類型轉換通常分為三組:提升、標准轉換和用戶定義的轉換。
62. 精確匹配細節:即使一個實參必須應用一些最小的類型轉換才能將其轉換為相應函數參數的類型;這些最小的類型轉換包括:左值到右值的轉換;從數組到指針;從函數到指針的轉換和限定類型轉換(通常是const和volatile類型轉換)。精確匹配可以使用一個顯式強制轉換強行執行,這時實參類型就為轉換後的類型。
C 基本語法(下)
0. 類型轉換中的提示實際上就是內置數據類型的提升,如char轉換為int,bool轉換為int,float轉換為double等。
1. 類型轉換中的標准轉換有五種類型:(1)整值類型轉換(不包括提升);(2)浮點轉換;(3)浮點-整值轉換;(4)指針轉換和(5)bool轉換。前三種轉換是有潛在危險的轉換。所有的標准轉換都是等價的。一些注意點:0可以被轉換為任何指針類型,這樣創建的指針稱為空指針值,同時0也可以是任何整型常量表達式。常量表達式0L及0x00都屬於整值類型因此能夠被轉換為int *的空指針值。指針轉換允許任何指針類型的實參轉換成void *,但是函數指針不能用標准轉換為void *。
2. 對於引用參數來說,如果實參是該引用的有效初始值,則為精確匹配,否則為不匹配。
3. 模板非類型參數代表了一個常量表達式,由一個普通的參數聲明構成,表示該參數名代表了模板定義種的一個常量。在實例化時,該常量會被一個編譯時已知的常量值代替。
4. 在模板中,為了支持類型表達式,必須使用typename;如typename Param::name *p;定義了一個指針p;如果不使用typename則該表達式為乘法。另外,模板函數也可以被聲明為inline或extern,必須把指示符放在參數模板後。如template <typename T> inline T fun1(){…}
5. 函數實參的推演中三種類型轉換是允許的:(1)左值轉換;(2)限定轉換(const和volatile指示符);(3)到一個基類的轉換:如果實參是一個類,它有一個從被指定為函數參數的類模板實例化而來的基類;如下面的代碼:template <typename T> class Array {}; template <typename T> T min(Array &array) {…};
6. 可以使用顯式指定模板參數,模板參數被顯式指定在逗號分隔的列表中,緊跟在函數模板實例的名字後面;如min<unsigned int>(a, b);代碼強行指定模板參數以unsigned int轉換。模板顯式指定參數類似於默認參數,只能省略後面的參數。
7. C 支持兩種模板編譯模式:包含模式和分離模式。包含模式下,在每個模板被實例化的文件中包含函數模板的定義,並且往往把定義放在頭文件中;分離模式下,函數模板的聲明被放在頭文件中,它的定義放在另外一個實現文件中,函數模板在實現文件中的定義必須使用關鍵字export。
8. C 支持模板的顯式實例化,在顯式實例化聲明所在的文件中,函數模板的定義必須被給出。如template <typename T> T f(T t); 顯式實例化為int *類型,template int *f(int *);
9. C 支持模板的顯式特化定義,如:template <typename T> T max(T t1, T t2){…}顯式特化const char *類型的定義:template <> const char *max<const char *>(const char *c1, const char *c2);顯式特化隱藏了通用模板對於該類型的實例。如果模板實參可以從函數參數中推演出來,則模板實參的顯式特化可以從其中取消。如上面的例子: template <> const char *max(const char *, const char *);其省略與顯式指定模板參數一樣。
10. 拋出異常可通過throw表達式來實現。如拋出一個popOnEmpty(類對象)的異常,表達式為throw popOnEmpty();同時在catch子句中,為了防止大型類對象的復制,可以將catch子句的異常聲明改為引用。
11. 在查找用來處理被拋出異常的catch子句時,因為異常而退出復合語句和函數定義,這個過程稱為棧展開。C 保證,隨著棧的展開,盡管局部類對象的生命期是因為拋出異常而結束,但是所有的析構函數將被調用。要重新拋出接受到的異常,使用語句throw;重新拋出的過程中希望對接受到的異常對象進行修改,必須將catch子句的異常聲明改為引用。
12. 異常規范跟隨在函數參數表之後,用throw指定,後面是異常類型表。如void pop(int &value) throw(popOnEmpty);異常規范保證不會拋出任何沒有出現在異常類型表中的異常。如果在運行時拋出一個沒有出現在異常類型表中的異常,則系統調用C 標准庫中定義的函數unexpected,該函數直接調用了terminate函數結束程序。空的異常規范不會拋出任何異常。
13. 異常規范可以在函數聲明中指出,當有異常規范的指針被初始化時,被用作右值的指針異常規范必須比用作左值的指針規范一樣或者更嚴格,當然,參數類型表必須相同。
14. 使用函數指針來使得template適應更多的場合。內置的函數指針聲明的模板定義,代碼如下: template< typename Type, bool (*Comp)(const Type&, const Type &)> const Type& min(const Type *p, Comp comp);該方案的主要缺點是函數調用使它無法內聯。使用函數對象來替代函數指針,函數對象是類實現,它重載了調用操作符,即operaotr ()函數。有兩個優點:(1)如果被重載的調用操作符是inline,則編譯器能執行內聯編譯;(2)函數對象可以擁有任意數目的額外數據來緩沖結果。改寫模板使之能同時接受函數指針和函數對象(缺點是沒有任何的原型檢查),template<typename Type, typename Comp> min(const Type *p, Comp comp);
Ⅶ java如何優化編譯呢
#java編譯器對`String常量表達式`的優化:
- 1.String+String 可以被編譯器識別為常量表達
String a="ab" ;
String b="a"+"b";//編譯後:b="ab"
System.out.println(a==b);//true
分析:
編譯器將"a"+"b"當做常量表達式,在編譯時期進行優化,直接取"ab". 在運行時期
並沒有創建新的對象,而是從jvm字元串常量池中獲取之前已經存在的"ab"對象.
- 2.String+基本類型 可以被編譯器識別為常量表達式
String a="a1";
String b="a"+1; //"a1"
String c="a"+true;//"atrue"
String d="a"+3.14;//"a3.14"
#java編譯器對`常量`優化:
* 它是編譯時的一項優化技術,將代碼的常量計算在編譯期完成,節約了運行時的計算量.
1.常量替換
//編譯前:
final int x=10;
int y=x;
//編譯後
int x=10;
int y=10;//編譯時,常量替換了
2.數學恆等式的模式匹配替換
//編譯前:
int x=10+10;
//編譯後
int x=20;//編譯時,模式匹配替換了
3.常量折疊
//編譯前:
boolean flag=true||(a || b && c);
//編譯後
boolean flag=true;//編譯時,常量折疊了
Ⅷ visual studio 如何在編譯期確定字串長度,特殊宏之類的
宏的作用,只是在編譯期間完成替代,並不能實現計算。因此不要考慮宏實現,這個沒什麼可行性。
編譯期間數值確定,其實可以通過const關鍵字實現,但說實話,這個是「消耗空間換時間」,如果有log的函數太多,太分散,並不值得。
如果log比較集中的在幾個函數中,那麼可以這么做:
constintfnamelen=strlen(__FUNCTION_);這樣,因為編譯之後__FUNCTION_是一個常量,const 變數會直接從編譯器的符號表中取值(這個叫C++的常量折疊),不會反復計算。
log(__FUNCTION_,fanmelen);
Ⅸ byte a=1;byte b=2;byte c=a+b;byte d=1+2; 請問第三句和第四句的計算機底層計算分別是怎麼樣的
分析:
d = 1 + 2; 1和2是常量,為固定不變的數據,在編譯的時候(編譯器javac),已經確定了1+2的結果並沒有超過byte類型的取值范圍,可以賦值給變數d,因此d=1+2 是正確的。
常量優化機制:
有一些計算,非常簡單,例如常量和常量的計算就非常簡單,在編譯階段就可以把這些簡單的運算計算完。
反之, c = a + b ,a和b是變數,變數的值是可能變化的,在編譯的時候,編譯器javac不確定 a + b 的結果是什麼,因此會將結果以int類型進行處理,所以int類型不能賦值給byte類型,因此編譯失敗。
Ⅹ java 中的編譯期常量是什麼使用它又什麼風險
編譯期常量就是所謂的 public final static 常量。
由於在編譯時就確定了值,在使用的場合會直接寫成值。而不是直接到原來的類中讀取。
這樣會有一個問題。 如果 類 A 提供了常量 類B使用了常量。並都進行了編譯。
然後,修改了類A的源碼,調用系統進行編譯。
系統發現類A是新的代碼,編譯了,類B是舊的代碼,就不進行編譯,使用舊的類。
由於類B中直接編譯進了舊的值,導致類A的修改無法反映到類B中。
這樣造成了風險。