青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Xiao.Zhu C++

Xiao.Zhu C++

  C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  29 隨筆 :: 14 文章 :: 17 評(píng)論 :: 0 Trackbacks

一般全局變量應(yīng)該用比較長(zhǎng),詳細(xì)的名稱,而本地變量則簡(jiǎn)單明了為宜。
GetSystemMetrics可以獲得很多系統(tǒng)信息。

#i nclude <>先搜索/I編譯選項(xiàng)的路徑,然后搜索環(huán)境變量INCLUDE,""先搜索父文件的路徑,然后才是前面兩個(gè)路徑。

用C設(shè)計(jì)一個(gè)動(dòng)態(tài)增長(zhǎng)的數(shù)組,需要用到MALLOC和REALLOC,對(duì)與同一個(gè)指針,如果內(nèi)存不夠存儲(chǔ)數(shù)據(jù),就用REALLOC重新分配多一倍的內(nèi)存。并且返回的指針不能直接給原來的指針,因?yàn)槿绻峙涫。敲丛瓉淼臄?shù)據(jù)就會(huì)丟失。

傳遞函數(shù)地址作為參數(shù)

首先定義以函數(shù)地址作為參數(shù)的函數(shù)

                  
fun1(返回類型(*fun2)(參數(shù)))
{
   ...=(*fun2)(參數(shù));

}
fun2正常定義
調(diào)用的時(shí)候  fun1(函數(shù)名);
int fun2(int n)
{
 return n+1;

}

int fun1(int n,int(*f)(int b))
{
 return (*f)(n);

 

}
int main(int argc, char* argv[])
{
 int a(B),b;
    b=fun1(a,fun2);
    cout<<b;
 return 0;
}

實(shí)現(xiàn)散列表:使用一個(gè)散列函數(shù),將項(xiàng)散列到一個(gè)數(shù)組里面,每個(gè)數(shù)組元素是一個(gè)鏈表,記錄這個(gè)散列值的所有項(xiàng)。

CMemoryState 類可用于檢查內(nèi)存泄露。

 

 

構(gòu)造函數(shù)和析構(gòu)函數(shù)都沒有返回值

類的成員缺省是私有的.
如果定義了構(gòu)造函數(shù)和析構(gòu)函數(shù),必須將它們?cè)O(shè)置為PUBLIC,否則無法訪問.
(事實(shí)上也可以是private,但只能訪問類的靜態(tài)成員,這時(shí)不能生成對(duì)象,如果要生成對(duì)象,需要一定的技巧)

重載依靠參數(shù)不同而不是返回值的不同,因?yàn)槲覀兛梢栽谡{(diào)用函數(shù)的時(shí)候忽略返回值,這個(gè)時(shí)候編譯器無法確定該調(diào)用那個(gè)函數(shù).

構(gòu)造函數(shù)可以根據(jù)參數(shù)類型不同區(qū)分,也可以根據(jù)參數(shù)個(gè)數(shù)不同區(qū)分,但如果一個(gè)構(gòu)造函數(shù)的參數(shù)有缺省值而且前面的參數(shù)與其它構(gòu)造函數(shù)相同,那么如果在調(diào)用的時(shí)候用到了缺省值,編譯器無法分辨該調(diào)用那個(gè)函數(shù),會(huì)報(bào)錯(cuò).

可以在定義函數(shù)的時(shí)候,讓某個(gè)參數(shù)只有類型名而沒有標(biāo)識(shí)符,這樣做的目的是為了將來可能需要插入一個(gè)參數(shù),在調(diào)用的時(shí)候隨便給這個(gè)位置一個(gè)值就可以了.

int temp(int a,int,int b)
{}

~按位求反

const 存放在符號(hào)表里,不會(huì)被分配內(nèi)存.如果使用&符號(hào),就會(huì)強(qiáng)迫編譯器為常量分配地址,const數(shù)組也會(huì)被分配內(nèi)存.

const int* p;int const* p;指向常量的指針,它的內(nèi)容(*p)不能被改變,也不能將它的值賦int * const p,常指針,值不變。給非常量指針。
對(duì)臨時(shí)對(duì)象要使用常值引用接收,因?yàn)榕R時(shí)對(duì)象是常量。

對(duì)于const成員變量,必須在構(gòu)造函數(shù)前(構(gòu)造函數(shù)初始化表)對(duì)它賦初值(莄onst也可以在這里賦值,但沒有必要,可以轉(zhuǎn)到構(gòu)造函數(shù)體內(nèi),雖然在初始化表里效率更高)。任何類型的成員變量都不能在聲明的時(shí)候賦值。

類中的常量const是對(duì)某個(gè)具體對(duì)象而言的,如果需要對(duì)整個(gè)類的常值,使用enum.

char *p="123456";
cout<<*p++<<*p++;
*p=3;
char *t;
t=p;
輸出結(jié)果為:21,因?yàn)榍笾淀樞蚴菑挠业阶?第三個(gè)語(yǔ)句是錯(cuò)的,*p的內(nèi)存是不可寫的,似乎p應(yīng)該為一個(gè)常量指針,但確實(shí)可以將p賦給一個(gè)非常指針,語(yǔ)句5不報(bào)錯(cuò).

如果成員函數(shù)被聲明為const,那么其中不能含有改變成員變量值的語(yǔ)句(但可以改變被mutable修飾的成員變量)),也不能調(diào)用非const成員函數(shù),如果對(duì)象被聲明為const,那么它只能調(diào)用const成員函數(shù).

volatile的用法同const,甚至可以使用const volatile做修飾沒,volatile標(biāo)識(shí)數(shù)據(jù)可能被別的進(jìn)程改變,因此有必要在每次使用的時(shí)候重讀這個(gè)數(shù)據(jù),這在優(yōu)化期間特別重要,防止編譯器做一些假設(shè).

給宏的參數(shù)最好是簡(jiǎn)單變量,如果不是,如a++,那么變量在宏中出現(xiàn)幾次,a就會(huì)被加多少次。最好不用宏做類似函數(shù)的事情,在類中,用內(nèi)聯(lián)函數(shù)代替宏,一樣可以得到高效率。

在類中定義的函數(shù)自動(dòng)成為內(nèi)聯(lián)函數(shù),在類外,使用inline關(guān)鍵字。

不應(yīng)該使用public成員變量,而是應(yīng)該使用內(nèi)聯(lián)函數(shù)存取這些變量,使用重載可以只用一個(gè)函數(shù)名字完成存取。

一個(gè)程序的所有文件,所有的名字(不在函數(shù)或類中)缺省都是外部連接,這意味著不同文件中相同的名字(不在函數(shù)或類中)會(huì)引起沖突,如果對(duì)這些名字用static修飾,就會(huì)變成內(nèi)部連接,名字僅在編譯單元內(nèi)可見。extern是static的反義詞,表示外部連接,同缺省的意義相同。兩種連接的名字都會(huì)存儲(chǔ)在靜態(tài)存儲(chǔ)區(qū)。

一旦用于修飾局部變量,static就不再表示可見性,而只改變變量的存貯類型。

register表示希望這個(gè)變量被放在寄存器里,因?yàn)樗?jīng)常被用到,應(yīng)該避免使用這個(gè)關(guān)鍵字,因?yàn)檫@方面通常機(jī)器比人更擅長(zhǎng)。

類中的static變量(包括用static修飾的const)必須在類和函數(shù)外的全局位置初始化,初始化的語(yǔ)法是:static 類型 類::變量名=值;

存在嵌套類和局部類:類中定義的類和函數(shù)中定義的類,后者不能有靜態(tài)成員變量(顯然,沒有辦法初始化這種static變量).

調(diào)用c庫(kù),在聲明的時(shí)候要使用extern "C" 函數(shù)聲明,指明這是一個(gè)c連接.因?yàn)閏++同c的編譯器不同,會(huì)為函數(shù)產(chǎn)生不同的內(nèi)部名,按照c++的方式連接c函數(shù),會(huì)找不到庫(kù)中的函數(shù)體.當(dāng)然,通常情況下庫(kù)的開發(fā)上已經(jīng)為我們做好了這些.

引用必須被初始化,且引用的對(duì)象不能改變.不能引用null.

int f(const int&)
上面是一個(gè)常量引用,使用常量引用是為了保證外部變量不被修改,另外,如果傳入的是常量或者臨時(shí)對(duì)象,不使用常量引用的參數(shù)將出錯(cuò).因?yàn)槎叨际浅A?

無返回值的函數(shù)是void類型.

void inf(int*&i){i++;}  調(diào)用:int *i=0; inf(i);
上面的函數(shù)是以指針引用做參數(shù),改變指針的值.還可以使用指向指針的指針,要麻煩一些,不過表達(dá)更明確:void inf(int **i){(*i)++}; 調(diào)用時(shí):int *i=0; inf(&i);

通過值傳遞給函數(shù),或者函數(shù)返回一個(gè)對(duì)象,是使用位拷貝建立對(duì)象,這種情況下編譯器會(huì)調(diào)用拷貝構(gòu)造函數(shù)(如果沒有編譯器會(huì)建立一個(gè)缺省的),但對(duì)象銷毀時(shí)會(huì)調(diào)用析構(gòu)函數(shù).

如果想禁止通過值傳遞某個(gè)對(duì)象,只要聲明一個(gè)私有的拷貝構(gòu)造函數(shù),此時(shí)編譯器認(rèn)為用戶接管了這項(xiàng)工作,不會(huì)建立缺省的拷貝構(gòu)造函數(shù),而用戶建立的函數(shù)是私有的,沒法調(diào)用,編譯器就會(huì)報(bào)錯(cuò).

可以定義指向類的成員變量和成員函數(shù)的指針,程序不必使用函數(shù)的名字就可以調(diào)用它,想起了高通的CDMA程序框架.c++編程思想第10章。

一個(gè)指向函數(shù)的指針:
void inf(int *&i){i++;}
int main(int argc, char* argv[])
{
int *i=0;
cout<<i<<endl;
void (*pf)(int *&);
pf=&inf;
(*pf)(i);
cout<<i<<endl;
}


運(yùn)算符重載
重載僅是對(duì)用戶類型的數(shù)據(jù)來說的,對(duì)內(nèi)置的數(shù)據(jù)類型是不可以重載運(yùn)算符的。
.和.*都不能重載.可以將運(yùn)算符重載看作另外一種形式的函數(shù)調(diào)用,函數(shù)的名字是operator@,@代表運(yùn)算符,參數(shù)的個(gè)數(shù)取決于兩個(gè)因素:
1 運(yùn)算符是一元還是二元
2 運(yùn)算符是全局函數(shù)(一元是一個(gè)參數(shù),二元是兩個(gè)參數(shù)),還是成員函數(shù)(一元沒有參數(shù),二元一個(gè)參數(shù)----對(duì)象變?yōu)樽髠?cè)參數(shù))

可以重載幾乎所有的運(yùn)算符,但對(duì)于現(xiàn)在c中沒有意義的運(yùn)算符是不能重載的,也不能改變運(yùn)算符的參數(shù)個(gè)數(shù)和優(yōu)先級(jí).

重載運(yùn)算符的返回值:如果需要返回對(duì)象本身,根據(jù)需要返回
對(duì)象的指針或者引用,如果是返回臨時(shí)生成的對(duì)象,那么返回對(duì)象.

重載運(yùn)算符的返回值是否常量:當(dāng)返回的是一個(gè)臨時(shí)值得時(shí)候,如:%,&,>>,這些運(yùn)算符得到的結(jié)果要賦給另外一個(gè)變量,這時(shí)返回值是const,如果返回值直接用于變量,如-=,+=,這是返回值不要加const.

函數(shù)返回對(duì)象的時(shí)候,返回一個(gè)臨時(shí)對(duì)象比新建一個(gè)對(duì)象在返回效率要高很多,因?yàn)檫@時(shí)調(diào)用的是普通構(gòu)造函數(shù)而不是拷貝構(gòu)造函數(shù),而且不需要調(diào)用析構(gòu)函數(shù),雖然新建一個(gè)對(duì)象再返回返回的也是一個(gè)臨時(shí)對(duì)象.

智能指針(smart pointer):對(duì)象,包容器,迭代器.

自動(dòng)類型轉(zhuǎn)換:可以編程實(shí)現(xiàn)自動(dòng)類型轉(zhuǎn)換.如需要從對(duì)象one到two,那么只需要為two定義一個(gè)以one&為參數(shù)的構(gòu)造函數(shù),當(dāng)編譯器發(fā)現(xiàn)需要進(jìn)行從對(duì)象one到two的轉(zhuǎn)換的時(shí)候,會(huì)自動(dòng)檢查two的定義,找到這個(gè)構(gòu)造函數(shù),構(gòu)造一個(gè)two對(duì)象.如果需要顯式類型轉(zhuǎn)換,在構(gòu)造函數(shù)前加一個(gè):explicit

還有一種自動(dòng)類型轉(zhuǎn)換方法是:為需要轉(zhuǎn)換的對(duì)象重載一個(gè)運(yùn)算符,運(yùn)算符以要轉(zhuǎn)換到的對(duì)象的名字命名.無須聲明返回值.
operator one() const{ return one(x);}


不過并不提倡隱式類型轉(zhuǎn)換,這樣容易隱藏錯(cuò)誤,也會(huì)降低調(diào)用時(shí)的效率.
 
使用全局重載運(yùn)算符而不是成員運(yùn)算符的好處是可以對(duì)左右操作書都自動(dòng)作類型轉(zhuǎn)換,而成員運(yùn)算符的操作數(shù)左側(cè)的必須是正確的對(duì)象

重載賦值操作符"=",返回可以是引用也可以是值,前者效率較高,但要記得此時(shí)返回的引用不能是屬于局部對(duì)象的.通常返回*this.

return String(s1+s2); 與String temp(s1+s2);return temp;的效率是不同的,后者要進(jìn)行對(duì)象拷貝,而前者直接將臨時(shí)對(duì)象創(chuàng)建在函數(shù)的返回區(qū)。同時(shí)也更加簡(jiǎn)潔。

函數(shù)中少用static變量。讓相同的輸入產(chǎn)生相同的輸出,這樣的代碼便于使用和維護(hù)。

對(duì)函數(shù)的參數(shù)和返回值的有效性進(jìn)行檢查。

積極使用斷言(ASSERT),同時(shí)要加上注釋,防止將來忘記ASSERT的目的。

之所以有了指針還要引入引用,是為了對(duì)功能加以限制,防止發(fā)生意外,就像對(duì)參數(shù)加上const限定的目的一樣。

動(dòng)態(tài)分配內(nèi)存的原則:
1 分配后要檢查是否分配成功,即if(p==NULL)
2 釋放內(nèi)存后要記得令p=NULL,防止產(chǎn)生野指針.野指針會(huì)讓我們?cè)谑褂弥羔樓暗膇f(p==NULL)檢查形同虛設(shè).

要申請(qǐng)一塊內(nèi)存復(fù)制數(shù)組char a[]的內(nèi)容,應(yīng)該申請(qǐng)的內(nèi)存大小是sizeof(char)*(strlen(a)+1);

如果通過參數(shù)傳遞數(shù)組,數(shù)組名自動(dòng)退化為一個(gè)指針.
main()
{
 char a[100];
        cout<<sizeof(a);
 fun(a);
}
void fun(char a[100])
{
   cout<<sizeof(a);
}
輸出100 4.

對(duì)內(nèi)存分配失敗進(jìn)行處理有兩種方法:
1 if(p==NULL) 適用于內(nèi)存分配語(yǔ)句較少的情況
2 _set_new_handler  _set_new_mode 適用于內(nèi)存分配語(yǔ)句較多的情況

unsigned與沒有unsigned 類型只是表示范圍不同,大小相同.

 

如果不給類定義拷貝構(gòu)造函數(shù)和賦值函數(shù),如果類中有指針變量,就會(huì)導(dǎo)致錯(cuò)誤,如果指針指向動(dòng)態(tài)內(nèi)存區(qū),那這塊內(nèi)存會(huì)丟失,而兩個(gè)指針相同一個(gè)塊內(nèi)存,導(dǎo)致其值無法判定,而且兩個(gè)函數(shù)的析構(gòu)函數(shù)會(huì)將這塊內(nèi)存釋放兩次,導(dǎo)致出錯(cuò)。
String a("hello");
String b("world");
String c(a); //調(diào)用拷貝構(gòu)造函數(shù),還可以寫成:String c=a;但風(fēng)格較差。
c=a;         //調(diào)用賦值函數(shù)(operator =) 賦值函數(shù)中注意先檢查自賦值。

在繼承當(dāng)中,構(gòu)造函數(shù),析構(gòu)函數(shù),賦值函數(shù)都不能被繼承,在編寫子類時(shí)要注以下幾點(diǎn):
1子類必須在構(gòu)造函數(shù)的初始化表調(diào)用基類的構(gòu)造函數(shù)。
2父類和子類的析構(gòu)函數(shù)都必須是virtual.//用于多態(tài)。
3子類賦值函數(shù)要調(diào)用父類的賦值函數(shù):Base::operater=(other);


對(duì)函數(shù)參數(shù)和返回值進(jìn)行const限定僅對(duì)指針和引用有意義,對(duì)值傳遞沒有意義,對(duì)輸出參數(shù)一定不要用const,不然無法輸出參數(shù)。

重載new和delete的原因有兩個(gè):需要反復(fù)分配內(nèi)存,需要親自做這個(gè)工作提高效率,還有就是減少內(nèi)存碎片,比如可以首先使用靜態(tài)成員指針保留很大一塊內(nèi)存(在靜態(tài)存儲(chǔ)區(qū)),在其中完成內(nèi)存的分配,并自己標(biāo)記分配和釋放,釋放的時(shí)候,只是標(biāo)記內(nèi)存,而不free釋放。
重載的new和delete只完成內(nèi)存的分配和回收工作。new接受size_t函數(shù),完成內(nèi)存的分配,返回一個(gè)void*指針,delete接受一個(gè)void*指針,將它釋放。
注意重載new和delete有兩種不同的形式,一個(gè)用于每次創(chuàng)建一個(gè)對(duì)象,另一個(gè)用來創(chuàng)建一個(gè)對(duì)象數(shù)組,需要加上[]。如果重載了前者,那么在創(chuàng)建對(duì)象數(shù)組的時(shí)候,系統(tǒng)會(huì)調(diào)用全局的new和delete.

發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象,可以使用值為NULL的指針調(diào)用任意對(duì)象的成員函數(shù),只要先強(qiáng)制轉(zhuǎn)換到這個(gè)對(duì)象,并且調(diào)用的是純代碼。

成員對(duì)象的初始化可以和父類構(gòu)造函數(shù)的調(diào)用并排放在初始化表。

在進(jìn)入構(gòu)造函數(shù)的左括號(hào)前,所有的成員變量都必須被初始化。

構(gòu)造函數(shù),析構(gòu)函數(shù),賦值運(yùn)算賦不被繼承。

類的友元能夠訪問其private,protected成員,子類能訪問類的protected成員。

不要在析構(gòu)函數(shù)中拋出異常,因?yàn)楫惓L幚砗瘮?shù)在獲得異常后要調(diào)用析構(gòu)函數(shù)清理對(duì)象,此時(shí)再發(fā)生異常會(huì)導(dǎo)致程序無法再捕獲異常,只能終止(只能在自定義的set_terminate()中作最后的處理。)。

拷貝字符串的方法
char dest[sz];
memset(dest,0,sz);
strncpy(dest,source,sz-1);
這樣保證了不會(huì)超過緩沖區(qū)且結(jié)尾為'\0'.

異常處理函數(shù)會(huì)首先調(diào)用所有在try塊中創(chuàng)建了的對(duì)象的析構(gòu)函數(shù),然后執(zhí)行異常處理函數(shù),然后繼續(xù)運(yùn)行后面的程序。但問題是,如果一個(gè)析構(gòu)函數(shù)出現(xiàn)了異常,在析構(gòu)函數(shù)中異常前創(chuàng)建的堆上的所有對(duì)象都無法調(diào)用其析構(gòu)函數(shù)正常銷毀。方法是使用模板,并自初始化表創(chuàng)建這些模板對(duì)象。

set_unexpceted可以截獲沒有被函數(shù)異常規(guī)格說明包括得異常,還可以簡(jiǎn)單的用一個(gè)throw;將這個(gè)異常作為已知異常再次拋出,如果有相應(yīng)的catch語(yǔ)句,那么就可以捕獲這個(gè)異常。

拋出異常的子類,會(huì)被能夠捕獲其父類異常的處理器捕獲。這時(shí)會(huì)產(chǎn)生切片,即處理器收到的是一個(gè)父類,使用引用而不是傳遞值可以避免這個(gè)問題。
      try
  {
   throw(except("got it"));
  }
  catch(except &t)
  {
   t.what();
  }


運(yùn)行時(shí)類形識(shí)別對(duì)void指針無效。

dynamic_cast<>用于向下映射。
base* b=new derived;
derived* d=dynamic_cast<derived*>b; 如果dynamic_cast失敗的話,將返回NULL,可以以此來試探著判斷指針b的類型。

RTTI還可以使用typeinfo().name()的方法返回對(duì)象id。typeinfo()返回typeinfo對(duì)象,使用前要包含頭文件typeinfo.h.

class B
class D:public B

B* p=new D;
B& r=*p;

typeid(p)==typeid(B*)
typeid(r)!==typeid(D)
typeid(*p)==typeid(D)
typeid(&r)==typeid(B*)

對(duì)引用的動(dòng)態(tài)映射也要制定到一個(gè)引用上,如果失敗不是返回NULL,因?yàn)閼?yīng)用不許為空,而是產(chǎn)生一個(gè)異常,因此對(duì)引用的動(dòng)態(tài)映射必須使用異常處理。

對(duì)空指針使用typeid()也會(huì)產(chǎn)生異常,可以在使用之前檢查指針是否為NULL來避免這個(gè)問題。

在對(duì)重繼承的情況下,傳統(tǒng)的強(qiáng)制類型轉(zhuǎn)換可能無法正常工作,但動(dòng)態(tài)映射和typeid工作的很好。

要是動(dòng)態(tài)類型類型轉(zhuǎn)換,需要基類包含virtual成員函數(shù),并且vc編譯器有/GR選項(xiàng)。經(jīng)過動(dòng)態(tài)類型轉(zhuǎn)換,由父類轉(zhuǎn)換而來的子類指針可以調(diào)用子類中新添加而父類中沒有的方法。

static_cast 通常不是必需的,但它會(huì)讓類型轉(zhuǎn)換更加醒目。

const_cast用于將常量和volatile映射給普通指針。

reinterpret_cast是危險(xiǎn)并且可移植性很差的轉(zhuǎn)換,它將對(duì)象看作二進(jìn)制數(shù)進(jìn)行轉(zhuǎn)換。最好不要使用。

C++中,將結(jié)構(gòu)名直接作為類型名使用,而不需要象c中那樣使用typedef  struct 結(jié)構(gòu)名{} 類型名;

                    WINDOWS核心編程
內(nèi)核對(duì)象:每個(gè)內(nèi)核對(duì)象都是一個(gè)內(nèi)存塊,由內(nèi)核維護(hù),進(jìn)程在創(chuàng)建了一個(gè)內(nèi)核對(duì)象后獲得一個(gè)句柄,通常一個(gè)進(jìn)程的句柄對(duì)另外一個(gè)進(jìn)程是沒有意義的,但可以通過一定措施在進(jìn)程間共享內(nèi)核對(duì)象。當(dāng)進(jìn)程終止后,它創(chuàng)建的內(nèi)核對(duì)象不一定消失,內(nèi)核維護(hù)每個(gè)內(nèi)核對(duì)象的引用計(jì)數(shù)。


GDI對(duì)象不是內(nèi)核對(duì)象,區(qū)分內(nèi)核對(duì)象和GDI對(duì)象的方法是內(nèi)核對(duì)象的創(chuàng)建函數(shù)的參數(shù)中有安全屬性,而GDI對(duì)象沒有。

內(nèi)核對(duì)象的安全屬性通常在創(chuàng)建服務(wù)器程序的時(shí)候用到,傳遞一個(gè)NULL可以獲得缺省的安全屬性。

當(dāng)不再使用某個(gè)內(nèi)核對(duì)象的時(shí)候,可以使用BOOL CloseHandle(HANDLE)關(guān)閉句柄,系統(tǒng)會(huì)自動(dòng)將內(nèi)核對(duì)象信息清除出進(jìn)程的句柄表(此句柄表保存且僅保存該進(jìn)程使用的所有內(nèi)核對(duì)象信息。),并自動(dòng)為內(nèi)核對(duì)象的引用計(jì)數(shù)減一。如果忘記關(guān)閉句柄也不要緊,在進(jìn)程推出后,系統(tǒng)會(huì)自動(dòng)檢查進(jìn)程的句柄表,清理沒有釋放的句柄,因此忘記關(guān)閉句不一定會(huì)造成內(nèi)存泄漏。

程序的進(jìn)入點(diǎn)WinMain的第一個(gè)參數(shù)時(shí)進(jìn)程的實(shí)例句柄,也是進(jìn)程映射到虛擬地址空間的起始地址,vc++默認(rèn)是0x00400000.可以用GetModuleHandle()得到這個(gè)值
PTSTR GetCommandLine()獲得命令行
PWSTR CommandLineToArgvW()分解命令行

每個(gè)進(jìn)程都有一個(gè)與他相關(guān)的環(huán)境塊。
VarName1=VarValue1\0
VarName2=VarValue2\0
...............
\0

GetEnvironmentVariable()  //獲得環(huán)境變量值
ExpandEnvironmentStrings()//展開%包裹的環(huán)境變量值
SetEnvironmentVariable()  //設(shè)定環(huán)境變量

進(jìn)程的親緣性:進(jìn)程的線程被強(qiáng)迫再CPU的子集上運(yùn)行。

子進(jìn)程默認(rèn)繼承父進(jìn)程的錯(cuò)誤標(biāo)志。
SetErrorMode(UINT)  //設(shè)定錯(cuò)誤模式

進(jìn)程維護(hù)當(dāng)前驅(qū)動(dòng)器和目錄信息
GetCurrentDirectory()
SetCurrentDirectory()

獲得系統(tǒng)版本:
GetVersion()
GetVersionEx()
VeryfyVersionInfo()


GetExitCodeProcess(),對(duì)于還在運(yùn)行的進(jìn)程,可以得到0x103(STILL_ACTIVE),對(duì)于終止的進(jìn)程,如果還沒有CloseHandle(pi.hProcess),可以得到它的退出碼,否則得到的是亂碼。

windows2000支持作業(yè)管理, 通過將進(jìn)程加入作業(yè),可以對(duì)進(jìn)程的運(yùn)行權(quán)限,使用的資源進(jìn)行限制。方法如下:
HANDLE hjob=CreateJobObject(NULL,NULL);//創(chuàng)建一個(gè)作業(yè)對(duì)象。
SetInformationJobObject();//設(shè)定作業(yè)對(duì)象的參數(shù),包括對(duì)進(jìn)程的各種限制。
CreateProcess(NULL,"CMD",NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);//創(chuàng)建新進(jìn)程。
AssignProcessToJobObject(hjob,pi.hProcess); //將進(jìn)程加入作業(yè)。可加入多個(gè)。
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
HANDLE h[2];
h[0]=pi.hProcess;
h[1]=hjob;
DWORD dw=WaitForMultipleObject(2,h,false,INFINITE);
switch(dw-WAIT_OBJECT_0)
  case 0://the process has terminated..
  case 1://all of the job's allotted cpu time was used.
}
CloseHandle(pi.hProcess);
CloseHandle(hjob);

終止作業(yè)中所有進(jìn)程的運(yùn)行
TerminateJobObject(hjob,UINT uExitCode)

查詢作業(yè)統(tǒng)計(jì)信息
QueryInformationJobObject();

監(jiān)視作業(yè)的運(yùn)行:
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;//創(chuàng)建一個(gè)I/O完成端口對(duì)象
SetInformationJobObject(hjob.JobObjectAssociateCompletionPortInformation,&joacp,sizeof(joacp)
//將作業(yè)同完成端口對(duì)相關(guān)聯(lián)。
GetQueuedCompletionStatus()  //監(jiān)控I/O端口。

進(jìn)程由兩部分組成:進(jìn)程內(nèi)核對(duì)象, 地址空間。進(jìn)程是不活潑的,它的執(zhí)行依賴于線程。
線程由兩部分組成:線程內(nèi)核對(duì)象,線程堆棧。


創(chuàng)建新線程:
DWORD WINAPI FUNC(PVOID pvParam)
int Param;
DWORD dwThreadID;
CreateThread(NULL,0,FUNC,(PVOID)&Param,0,&dwThreadID);

檢查線程是否退出:
BOOL GetExitCodeThread(HANDLE hThread,PDWORD pdwExitCode);//如果還未終止,得到0x103.


獲得偽句柄:
GetCurrentProcess()
GetCurrentThread()

獲得運(yùn)行時(shí)間:
GetProcessTimes()
GetThreadTimes()

線程或進(jìn)程的偽句柄轉(zhuǎn)化為實(shí)句柄:
DuplicatgeHandle();//此函數(shù)會(huì)增加內(nèi)核對(duì)象的引用計(jì)數(shù)。

偽句柄用于本線程,獲得這個(gè)句并不會(huì)影響內(nèi)核對(duì)象的計(jì)數(shù),而實(shí)句柄用于傳遞給子進(jìn)程。

線程的暫停和運(yùn)行:
ResumeThread(HANDLE)
SuspendThread(HANDLE)  //使用此函數(shù)要小心死鎖。

線程休眠:
Sleep(DWORD dwMilliseconds);
自動(dòng)退出當(dāng)前時(shí)間片:
SwitchtoThread();

可以獲得和修改線程的上下文,使用之前要SuspendThread()
GetThreadContext()
SetThreadContext()

改變進(jìn)程的優(yōu)先級(jí)://記住進(jìn)程是不可以調(diào)度的,調(diào)度的單位是線程。
BOOL SetPriorityClass();
DWORD GetPriorityClass();

設(shè)定線程的相對(duì)優(yōu)先級(jí):
int GetThreadPriority(HANDLE hThread);
BOOL SetThreadPriority(Handle hThread,int nPriority);

Microsoft保留了隨時(shí)修改調(diào)度算法的權(quán)利,因此使用相對(duì)優(yōu)先級(jí),可以保證程序在將來的系統(tǒng)上也可以正常運(yùn)行。
結(jié)合進(jìn)程優(yōu)先級(jí)和線程的相對(duì)優(yōu)先級(jí),就可以得到線程的基本優(yōu)先級(jí)。線程的當(dāng)前優(yōu)先級(jí)不可以低于基本優(yōu)先級(jí),
也就是說,系統(tǒng)會(huì)提高線程的優(yōu)先級(jí),并隨著執(zhí)行時(shí)間片的流逝降低優(yōu)先級(jí),但降到基本優(yōu)先級(jí)后就不再降了。
優(yōu)先級(jí)0-15成為動(dòng)態(tài)優(yōu)先級(jí)范圍,高于15是實(shí)時(shí)范圍,系統(tǒng)不會(huì)調(diào)度實(shí)時(shí)范圍線程的優(yōu)先級(jí),也不會(huì)把動(dòng)態(tài)優(yōu)先級(jí)范圍的
線程提高到15以上。

親緣性是對(duì)多處理器系統(tǒng)來說的,為了能利用保留在cpu高速緩存和NUMA(非統(tǒng)一內(nèi)存訪問)結(jié)構(gòu)計(jì)算機(jī)本插件板上內(nèi)存中的數(shù)據(jù),系統(tǒng)盡量線程上次運(yùn)行使用的CPU來運(yùn)行線程,包括軟親緣性(WIN2000默認(rèn))和硬親緣性(用戶可以選擇CPU)
相關(guān)的函數(shù)有:
BOOL SetProcessAffinityMask(HANDLE hProcess,DWORD_PTR dwProcessAffinityMask);
BOOL GetProcessAffinityMask(Handle hProcess,PDWORD_PTR pdwProcessAffinityMask,PDWORD_PTR pdwSystemAffinityMask);
DWORD_PTR SetThreadAffinityMask(HANDLE hThread,DWORD_PTR dwThreadAffinityMask);
DWORD_PTR SetThreadIdealProcessor(HANDLE hThread,DWORD dwIdealProcessor);

臨界區(qū)保證其中的資源(通常是各種共享變量)被原子的訪問,當(dāng)進(jìn)入臨界區(qū)后,其他訪問這些資源的線程將不會(huì)被調(diào)度。

線程同步包括用戶方式和內(nèi)核方式,用戶方式包括原子操作和臨界區(qū),它的特點(diǎn)是速度快,但功能有限。內(nèi)核方式利用內(nèi)核對(duì)象的通知狀態(tài)來同步線程,由于需要由用戶方式切換到內(nèi)核方式(這種切換很廢時(shí)間),且系統(tǒng)要進(jìn)行很多操作,效率較低,但功能強(qiáng)大(能夠設(shè)定超時(shí)值等,可以同步多個(gè)進(jìn)程的線程)。

內(nèi)核方式同步的原理:線程使自己進(jìn)入休眠狀態(tài),等待內(nèi)核對(duì)象由未通知狀態(tài)變?yōu)橐淹ㄖ獱顟B(tài)。

可處于未通知狀態(tài)變和已通知狀態(tài)的內(nèi)核對(duì)象:進(jìn)程,線程,作業(yè),文件修改通知,時(shí)間,可等待定時(shí)器,文件,控制臺(tái)輸入,信號(hào)量,互斥體。

進(jìn)程和線程在建立時(shí)處于未通知狀態(tài),在退出時(shí)變?yōu)橐淹ㄖ獱顟B(tài)。

等待函數(shù):
DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds);
DWORD WaitForMultipleObject(DWORD dwCount,CONST HANDLE* phObjects,BOOL fWaitALL,DWORD dwMilliseconds);其中,
0<dwCount<WAIT_OBJECTS(windows頭文件中定義為64),如果設(shè)定fWaitALl為TRUE,那么函數(shù)會(huì)知道左右對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)才會(huì)返回,如果傳遞FALSE,那么只要有一個(gè)對(duì)象變?yōu)橐淹ㄖ獱顟B(tài),函數(shù)就會(huì)返回。
返回值的含義:
HANDLE h[3];
h[0]=hProcess1;
h[1]=hProcess2;
h[2]=hProcess3;
DWORD dw=WaitForMultipleObject(3,h,FALSE,5000);
switch(dw)
{
    case WAIT_FAILED://Bad call to function(invalid handle?)
         break;
    case WAIT_TIMEOUT://None of the object became signaled within 5000 milliseconds.
         break;
    case WAIT_OBJECT_0+0:The process identified by h[0] terminated.
         break;
    case WAIT_OBJECT_0+1:
         break;
    case WAIT_OBJECT_0+2:
         break;
}
//WaitForSingleObject()的返回值只有前三種情況。如果給WaitForMutipleObject()的fWaitAll參數(shù)傳遞TRUE,那么其返回值也只有前三種。

事件內(nèi)核對(duì)象:有兩種,人工事件對(duì)象:當(dāng)它得到通知的時(shí)候,所有等待的線程都變?yōu)榭烧{(diào)度線程;自動(dòng)重置的事件:當(dāng)事件得到通知的時(shí)候,只有一個(gè)等待線程變?yōu)榭烧{(diào)度的線程。創(chuàng)建事件內(nèi)核對(duì)象:
HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa,BOOL fManualReset,BOOL fInitialState,PCTSTR pszName);
將事件改為通知狀態(tài):
BOOL SetEvent(HANDLE hEvent);
將事件改為未通知狀態(tài):
BOOL ResetEvent(HANDLE hEvent);
如果事件是自動(dòng)重置事件,那么成功等待會(huì)產(chǎn)生副作用,即將事件自動(dòng)置為未通知狀態(tài)。如果是人工事件對(duì)象,則沒有副作用。

等待定時(shí)器內(nèi)核對(duì)象:是在某個(gè)時(shí)間或按規(guī)定的間隔時(shí)間發(fā)出自己的信號(hào)通知的內(nèi)核對(duì)象。
HANDLE CreateWaitableTimer(PSECURITY_ATTRIBUTES psa,BOOL fManualReset,PCSTR pszName);
初始總是未通知狀態(tài)。
BOOL SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *pDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine,
BOOL fResume);
取消定時(shí)器:
BOOL CancelWaitableTimer(HANDLE hTimer);
如果僅想改變報(bào)時(shí)條件,不用調(diào)用這個(gè)函數(shù)暫停報(bào)時(shí)器,直接調(diào)用SetWaitableTimer()就可以了。

信號(hào)量?jī)?nèi)核對(duì)象
如果當(dāng)前資源的數(shù)量大于0,發(fā)出信號(hào)
如果當(dāng)前資源數(shù)量等于0,不發(fā)出信號(hào)
決不允許資源數(shù)量為負(fù)值。
創(chuàng)建信號(hào)量:
HANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount,
PCSTR pszName);
遞增資源:
BOOL ReleaseSemaphore(HANDLE hsem,
LONG lReleaseCount,
PLONG plPreviousCount);

互斥體內(nèi)核對(duì)象:互斥體確保對(duì)單個(gè)資源的互斥訪問。它包含一個(gè)使用數(shù)量,一個(gè)線程ID,一個(gè)遞歸計(jì)數(shù)器
與臨界區(qū)的區(qū)別:能夠同步多個(gè)進(jìn)程中的線程,可以設(shè)定超時(shí)值。
如果ID為0,那么表示沒有線程占用互斥體,互斥體發(fā)出信號(hào)。
如果ID不為0,表示占用資源的線程ID,不發(fā)出信號(hào)。

HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);

釋放資源:
BOOL ReleaseMutex(HANDLE hMutex);

額外的函數(shù):
DWORD SingalObjectAndWait(
HANDLE hObjectToSignal,
HANDLE hObjectToWaitOn,
DWORD dwMilliseconds,
BOOL fAlertable);
發(fā)出一個(gè)通知信號(hào)并等待另一個(gè)通知,效率比分別操作提高很多。

windows2000提供了如下幾種線程池函數(shù)用于線程管理:
一、異步調(diào)用函數(shù):
BOOL QueueUserWorkItem(
PTHREAD_START_ROUTINE pfnCallback,
PVOID pvContext,
ULONG dwFlags);
該函數(shù)將“工作項(xiàng)目”放入線程池并且立即返回。工作項(xiàng)目是指一個(gè)用pfnCallback參數(shù)標(biāo)識(shí)的函數(shù)。它被調(diào)用并且傳遞單個(gè)參數(shù)pvContext.工作項(xiàng)目函數(shù)原型如下:
DWORD WINAPI WorkItemFunc(PVOID pvContext);
dwFlags參數(shù):WT_EXECUTEDEFAULT  工作項(xiàng)目放入非I/O組件得線程中
             WT_EXECUTEINIOTHREAD 工作項(xiàng)目放入I/O組件的線程中,這樣的線程在I/O請(qǐng)求沒有完成之前不會(huì)被終止運(yùn)行                                  ,防止因?yàn)榫€程被終止導(dǎo)致I/O請(qǐng)求丟失。
             WT_EXECUTEINPERSISTENTTHREAD 放入永久線程池,
             WT_EXECUTELONGFUNCTION  工作項(xiàng)目需要長(zhǎng)時(shí)間的工作,系統(tǒng)會(huì)據(jù)此安排更多的線程。

線程池不能設(shè)置線程個(gè)數(shù)的上限,否則排隊(duì)個(gè)數(shù)超過線程個(gè)數(shù)上限的時(shí)候,會(huì)導(dǎo)致所有的線程都被中斷。

工作項(xiàng)目函數(shù)如果訪問了已經(jīng)被卸載的DLL,會(huì)產(chǎn)生違規(guī)訪問。


二、按規(guī)定的時(shí)間間隔調(diào)用函數(shù)
創(chuàng)建定時(shí)器隊(duì)列:
HANDLE CreateTimerQueue();
在隊(duì)列中創(chuàng)建定時(shí)器:
BOOL CreateTimerQueueTimer(
PHANDLE phNewTimer,
HANDLE hTimerQueue,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
DWORD dwDueTime,
DWORD dwPeriod,
ULONG dwFlags);
工作回調(diào)函數(shù)原型如下:
VOID WINAPI WaitOrTimerCallback(
PVOID pvContext,
BOOL fTimerOrWaitFired);
dwFlags比前面的多了一個(gè)標(biāo)志:WT_EXECUTEINTIMERTHREAD,表示由組件的定時(shí)器線程(定時(shí)器組件只有一個(gè)線程)運(yùn)行這個(gè)
工作函數(shù),此時(shí)的工作函數(shù)必須是很快返回的,否則定時(shí)器組件將無法處理其他的請(qǐng)求。

刪除定時(shí)器:
BOOL DeleteTimerQueueTimer(
HANDLE hTimerQueue,
HANDLE hTimer,
HANDLE hCompletionEvent);
在定時(shí)器線程中刪除定時(shí)器會(huì)造成死鎖。設(shè)定hCompletionEvent為INVALID_HANDLE_VALUE,那么在定時(shí)器的所有排隊(duì)工作項(xiàng)目沒有完成之前,DeleteTimerQueueTimer不會(huì)返回,也就是說在工作項(xiàng)目中對(duì)定時(shí)器進(jìn)行中斷刪除會(huì)死鎖。可以給hCompletionEvent傳遞事件句柄,函數(shù)會(huì)立即返回,在排隊(duì)工作完成之后,會(huì)設(shè)置該事件。

重新設(shè)定定時(shí)器://不能修改已經(jīng)觸發(fā)的單步定時(shí)器。
BOOL ChangeTimerQueueTimer(
HANDLE hTimerQueue,
HANDLE hTimer,
ULONG dwDueTime,
ULONG dwPeriod;

刪除定時(shí)器隊(duì)列:
BOOL DeleteTimerQueueEx(
HANDLE hTimerQueue,
HANDLE hCompletionEvent);

三、當(dāng)單個(gè)內(nèi)核對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)時(shí)調(diào)用函數(shù)
BOOL RegisterWaitForSIngleObject(
PHANDLE phNewWaitObject,
HANDLE hObject,
WAITORTIMERCALLBACK pfnCallback,
PVOID pvContext,
ULONG dwMilliseconds,
ULONG dwFlags);
pfnCallBack原型:
VOID WINAPI WaitOrTimerCallbadkFunc(
PVOID pvContext,
BOOLEAN fTimerorWaitFired);
如果等待超時(shí),fTimerorWaitFired==TRUE,如果是已通知狀態(tài),則為FALSE.

dwFlags可以傳遞參數(shù):WT_EXECUTEINWAITTHREAD,它讓等待組件得線程之一運(yùn)行工作項(xiàng)目函數(shù)。注意項(xiàng)同前。

如果等待的內(nèi)核對(duì)象是自動(dòng)重置的,那么會(huì)導(dǎo)致工作函數(shù)被反復(fù)調(diào)用,傳遞WT_EXECUTEONLYONCE會(huì)避免這種情況。

取消等待組件的注冊(cè)狀態(tài):
BOOL UnregisterWaitEx(
HANDLE hWaitHandle,
HANDLE hCompletionEvent);

四、當(dāng)異步I/O請(qǐng)求完成時(shí)調(diào)用函數(shù)
將設(shè)備和線程池的非I/O組件關(guān)聯(lián)
BOOL BindIoCompletionCallback(
HANDLE hDevice,
POVERLAPPED_COMPLETION_ROUTINE pfnCallback,
ULONG dwFlags//始終為0);

工作函數(shù)原型:
VOID WINAPI OverlappedCompletionRoutine(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransferred,
                         ,
POVERLAPPED pOverlapped);

Windows的內(nèi)存結(jié)構(gòu)

從98,2000,到64位的windows,內(nèi)存管理方式都是不同的,32位的win2000用戶內(nèi)存是從0x10000到0x7fffffff(64kB-2G),2000 Advanced server可以達(dá)到(64kB-3G),其中最高64kB也是禁止進(jìn)入的。再往上則由系統(tǒng)使用。98則是從0x400000-0x7fffffff(4M-2G),2G-3G是系統(tǒng)用來存放32位共享數(shù)據(jù)的地方,如很多系統(tǒng)動(dòng)態(tài)連接庫(kù)。0-4M是為了兼容16位程序保留的。3G-4G由系統(tǒng)自身使用。98的內(nèi)核區(qū)是不受保護(hù)的,2000受保護(hù)。
對(duì)虛擬地址空間的分配稱作保留,使用虛擬內(nèi)存分配函數(shù)(VirtualAlloc),釋放使用VirtualFree(),目前,所有cpu平臺(tái)的分配粒度都是64kB,頁(yè)面大小則不同,x86是4kB,Alpha是8kB,系統(tǒng)在保留內(nèi)存的時(shí)候規(guī)定要從分配粒度邊界開始,并且是頁(yè)面的整數(shù)倍,用戶使用VirtualAlloc都遵守這個(gè)規(guī)定,但系統(tǒng)不是,它是從頁(yè)面邊界開始分配的。

將物理存儲(chǔ)器映射到保留的內(nèi)存區(qū)域的過程稱為提交物理存儲(chǔ)器,提交是以頁(yè)面為單位進(jìn)行的,也使用VirtualAlloc函數(shù)。

物理存儲(chǔ)器是由內(nèi)存和(硬盤上的)頁(yè)文件組成的,如果訪問的數(shù)據(jù)是在頁(yè)文件中,則稱為頁(yè)面失效,cpu會(huì)把訪問通知操作系統(tǒng),操作系統(tǒng)負(fù)責(zé)將數(shù)據(jù)調(diào)入內(nèi)存,并指導(dǎo)cpu再次運(yùn)行上次失效的指令。

當(dāng)啟動(dòng)一個(gè)程序的時(shí)候,系統(tǒng)并不是將整個(gè)文件讀入內(nèi)存或者頁(yè)文件,而是將這個(gè)文件直接映射到虛擬內(nèi)存空間,并將需要的數(shù)據(jù)讀入內(nèi)存,即將硬盤上的文件本身當(dāng)作頁(yè)文件(雖然不是)。當(dāng)硬盤上的一個(gè)程序的文件映像(這是個(gè)exe文件或者dll文件)用作地址空間的物理存儲(chǔ)器,它稱為內(nèi)存映射文件。當(dāng)一個(gè).exe或者dll文件被加載時(shí),系統(tǒng)將自動(dòng)保留一個(gè)地址空間的區(qū)域,并將該文件映射到該區(qū)域中。但系統(tǒng)也提供了一組函數(shù),用于將數(shù)據(jù)文件映射到一個(gè)地址空間的區(qū)域中。


物理存儲(chǔ)器的頁(yè)面具有不同的保護(hù)屬性:
PAGE_NOACESS
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
PAGE_WRITECOPY
PAGE_EXECUTE_WRITECOPY
后兩個(gè)屬性是配合共享頁(yè)面機(jī)制使用的。WINDOWS支持多個(gè)進(jìn)程共享單個(gè)內(nèi)存塊,比如運(yùn)行notepad的10個(gè)實(shí)例,可以讓他們共享應(yīng)用程序的代碼和數(shù)據(jù),這樣可以大大提高性能,但要求該內(nèi)存塊是不可寫的。于是系統(tǒng)在調(diào)入.exe或者dll的時(shí)候,會(huì)計(jì)算那些頁(yè)面是可以寫入的,為這些頁(yè)面分配虛擬內(nèi)存。然后同其他的頁(yè)面一起映射到一塊虛擬內(nèi)存,但賦PAGE_WRITECOPY或者PAGE_EXECUTE_WRITECOPY屬性(通常包含代碼的塊是PAGE_EXECUTE_READ,包含數(shù)據(jù)的塊是PAGE_READWRITE)。當(dāng)一個(gè)進(jìn)程試圖將數(shù)據(jù)寫入共享內(nèi)存塊時(shí),系統(tǒng)會(huì)進(jìn)行如下操作:尋找預(yù)先分配的一個(gè)空閑頁(yè)面,將試圖修改的頁(yè)面拷貝到這個(gè)空閑頁(yè)面,賦予PAGE_READWRITE或者PAGE_EXECUTE_READWRITE屬性,然后更新進(jìn)程的頁(yè)面表,使得用戶可以對(duì)新的頁(yè)面進(jìn)行寫入。

還有三個(gè)特殊的保護(hù)屬性:PAGE_NOCACHE PAGE_WRITECOMBINE PAGE_GUARD,前兩個(gè)用于驅(qū)動(dòng)程序開發(fā),最后一個(gè)可以讓應(yīng)用程序在頁(yè)面被寫入的時(shí)候獲得一個(gè)異常。

塊的意思是一組相鄰的頁(yè)面,它們具有相同的保護(hù)屬性,并且受相同類型的物理存儲(chǔ)器支持。
賦予虛擬內(nèi)存頁(yè)面保護(hù)屬性的意義是為了提高效率,而且這個(gè)屬性總會(huì)被物理存儲(chǔ)器的保護(hù)屬性取代。

如果數(shù)據(jù)在內(nèi)存中沒有對(duì)齊,那么cpu要多次訪問才能得到數(shù)據(jù),效率很低。

內(nèi)存管理函數(shù):

獲得系統(tǒng)信息:
VOID GetSystemInfo(LPSYSTEM_INFO psinf);//可以得到頁(yè)面大小,分配粒度,最大內(nèi)存地址,最小內(nèi)存地址。

獲得內(nèi)存狀態(tài):
VOID GlobalMemoryStatus(LPMEMORYSTATUS pmst);

獲得內(nèi)存地址的某些信息:
DWORD VirtualQuery(
LPVOID pvAddress,
PMEMORY_BASIC_INFORMATION pmbi,
DWORD dwLength);

DWORD VirtualQuery(
HANDLE hProcess,
LPVOID pvAddress,
PMEMORY_BASIC_INFORMATION pmbi,
DWORD dwLength);

內(nèi)存映射文件的優(yōu)點(diǎn):
1 節(jié)省頁(yè)面文件;
2 加快程序啟動(dòng);
3 在多個(gè)進(jìn)程間共享數(shù)據(jù)。

進(jìn)程的啟動(dòng)過程:
系統(tǒng)首先將.exe文件映射到地址空間,缺省基地址是0x400000,然后查詢.exe的輸入表,將其使用的所有.dll也映射到地址空間(基地址在每個(gè).dll文件中,如果不能滿足,需要重定位),然后將執(zhí)行.exe的啟動(dòng)代碼。此時(shí).exe文件還在硬盤上。每次代碼跳到一個(gè)尚未加載到內(nèi)存的指令地址,就會(huì)出現(xiàn)一個(gè)錯(cuò)誤,系統(tǒng)會(huì)發(fā)現(xiàn)這個(gè)錯(cuò)誤,并將代碼加再到內(nèi)存中。
如果再創(chuàng)建這個(gè).exe文件的一個(gè)實(shí)例。那么直接將原來的地址空間中的內(nèi)容映射到新的地址空間就可以了。這樣多個(gè)實(shí)例就可以共享相同的代碼和數(shù)據(jù)。如果某個(gè)實(shí)例要改變共享內(nèi)容,系統(tǒng)就為要更改的頁(yè)面申請(qǐng)一個(gè)新的頁(yè)面,將內(nèi)容拷貝一份,然后用新的頁(yè)面代替地址空間中原來頁(yè)面的映射就可以了。98同2000不同,它不待修改便立即為所有的實(shí)例分配新的頁(yè)面。

使用內(nèi)存映射文件:
1 創(chuàng)建或打開一個(gè)文件內(nèi)核對(duì)象:
HANDLE CreateFile(
PCSTR pszFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
PSECURITY_ATTRIBUTES psa,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
失敗的返回值是INVALID_HANDLE_VALUE
2 創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象:
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
如果給函數(shù)的fdwProtect傳遞PAGE_READWRITE標(biāo)志,那么磁盤上文件的大小會(huì)變?yōu)橥诚裎募嗤笮 ?br>失敗的返回值是NULL。

3 將文件映射到進(jìn)程的地址空間:
PVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
windows2000會(huì)根據(jù)要求將部分文件映射到地址空間,而win98總是把全部?jī)?nèi)容映射到地址空間,并且僅能映射到2G-3G空間,此空間為共享空間,所有的進(jìn)程如果映射相同的文件,那么都會(huì)映射到相同的地址,一個(gè)進(jìn)程甚至不必映射就可以訪問這個(gè)空間里其他進(jìn)程的映射文件,win2000多個(gè)進(jìn)程映射同一個(gè)文件返回的地址通常是不同的。
4 從進(jìn)程的地址空間中撤銷文件數(shù)句的映像
BOOL UnmapViewOfFile(PVOID pvBaseAddress);

將文件映像寫入磁盤:
BOOL FlushViewOfFile(
PVOID pvAddress,
SIZE_T dwNumberOfBytesToFlush);

windows保證單個(gè)文件映射對(duì)象的多個(gè)視圖具有相關(guān)性。但不保證但個(gè)文件的多個(gè)映射對(duì)象有相關(guān)性。

使用MapViewOfFileEx代替MapViewOfFile可以設(shè)定文件映射的基地址:
PVOID MapViewOfFileEx(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
PVOID pvBaseAddress);

使用內(nèi)存映射文件在進(jìn)程間共享數(shù)據(jù)
共享機(jī)制:RPC ,COM,OLE,DDE,窗口消息(WM_COPYDATA),剪貼板,郵箱,管道,套接字。
在單機(jī)上,它們的底層實(shí)現(xiàn)方法都是內(nèi)存映射文件。

可以在頁(yè)文件中直接創(chuàng)建文件映射對(duì)象,方法是給CreateFileMapping函數(shù)的hFile參數(shù)傳遞INVALID_HANDLE_VALUE.注意,
CreateFile()函數(shù)運(yùn)行失敗也會(huì)返回這個(gè)參數(shù),因此一定要檢查CreateFile()的返回值。記住,文件函數(shù)運(yùn)行失敗的可能性太大了。

第三章:多個(gè)進(jìn)程共享對(duì)象。

堆棧:優(yōu)點(diǎn):可以不考慮分配粒度和頁(yè)面邊界之類的問題,集中精力處理手頭的任務(wù),缺點(diǎn)是:分配和釋放內(nèi)存塊的速度比其他機(jī)制慢,并且無法直接控制物理存儲(chǔ)器的提交和回收。

進(jìn)程的默認(rèn)堆棧是1MB,可以使用/HEAP鏈接開關(guān)調(diào)整大小,DLL沒有相關(guān)的堆棧。

堆棧的問題在于:很多windows函數(shù)要使用臨時(shí)內(nèi)存塊,進(jìn)程的多個(gè)線程要分配內(nèi)存塊,這些內(nèi)存都是在默認(rèn)堆棧上分配的,但規(guī)定時(shí)間內(nèi),每次只能由一個(gè)線程能夠分配和釋放默認(rèn)堆棧的內(nèi)存塊,其他想要處理內(nèi)存塊的線程必須等待。這種方法對(duì)速度又影響。可以為進(jìn)程的線程創(chuàng)建輔助堆棧,但windows函數(shù)只能使用默認(rèn)堆棧。

獲取進(jìn)程默認(rèn)堆棧句柄:
HANDLE GetProcessHeap();

創(chuàng)建輔助堆棧的理由
1 保護(hù)組件:
多個(gè)組件的數(shù)據(jù)混合交叉的存放在一塊內(nèi)存里,那么一個(gè)組件的錯(cuò)誤操作很容易影響到另外一個(gè)組件。而要定位錯(cuò)誤的來源將十分困難。
2 更有效的內(nèi)存管理
通過在堆棧中分配同樣大小的對(duì)象,可以更加有效的管理內(nèi)存,減少內(nèi)存碎片。
3 進(jìn)行本地訪問:
將同種數(shù)據(jù)集中到一定的內(nèi)存塊,可以在操作的時(shí)候訪問較少的頁(yè)面,這就減少了RAM和硬盤對(duì)換的可能.
4 減少線程同步的開銷:
通過告訴系統(tǒng)只有一個(gè)線程使用堆棧(創(chuàng)建堆棧時(shí)使用HEAP_NO_SERIALIZE標(biāo)志給fdwOptions),可以避免堆棧函數(shù)執(zhí)行額外的用于保證堆棧安全性的代碼,提高效率,但此時(shí)用戶必須自己維護(hù)線程的安全性,系統(tǒng)不再對(duì)此負(fù)責(zé)。
5 迅速釋放堆棧。
因?yàn)閿?shù)據(jù)單一,因此釋放的時(shí)候只要釋放堆棧即可,不必顯示的釋放每個(gè)內(nèi)存塊。

創(chuàng)建輔助堆棧:
HANDLE HeapCreate(
DWORD fdwOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize);

從堆棧中分配內(nèi)存:
PVOID HeapAlloc(
HANDLE hHeap,
DWORD fdwFlags,
SIZE_T dwBytes);注意:當(dāng)分配超過(1MB)內(nèi)存塊的時(shí)候,最好使用VirtualAlloc();

改變內(nèi)存塊的大小:
PVOID HeapReAlloc(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem,
SIZE_T dwBytes);

檢索內(nèi)存塊的大小:
SIZE_T HeapSize(
HANDLE hHeap,
DWORD fdwFlags,
LPVOID pvMem);

釋放內(nèi)存塊:
BOOL HeapFree(
HANDLE hHeap,
DWORD fdwFlags,
PVOID pvMem);

撤銷堆棧:
BOOL HeapDestroy(HANDLE hHeap);

使用輔助堆棧的方法:重載對(duì)象的new操作符,在輔助堆上分配內(nèi)存,并給對(duì)象添加一個(gè)靜態(tài)變量用于保存堆句柄。

其它堆棧函數(shù):
獲取進(jìn)程中所有堆棧得句柄:
DWORD GetProcessHeaps(DWORD dwNumHeaps,PHANDLE pHeaps);
驗(yàn)證堆棧完整性:
BOOL HeapValidate(
HANDLE hHeap,
DWORD fdwFlags,
LPCVOID pvMem);
合并地址中的空閑塊
UINT HeapCompact(
HANDLE hHeap,
DWORD fdwFlags);

BOOL HeapLock(HANDLE hHeap);
BOOL HeapUnlock(HANDLE

遍歷堆棧:
BOOL HeapWalk(
HANDLE hHeap,
PProcess_HEAP_ENTRY pHeapEntry);

各個(gè)dll也可以有自己的輸入表。

如何編寫DLL:
在DLL的頭文件中,有如下代碼:
#ifdef MYLIB
#else
    #define MYLIB extern "C" __declspec(dllimport)   
#endif
在每個(gè)輸出變量和輸出函數(shù)的聲明前,用MYLIB修飾。
在DLL的實(shí)現(xiàn)文件中,有如下代碼:
#i nclude "windows.h"
#define MYLIB extern "C" __declspec(dllexport)
#i nclude "Mylib.h"

其它的同編寫普通C++程序完全相同。 "C" 表示按C方式鏈接和調(diào)用函數(shù)。C++編譯器缺省按照__stdcall方式編譯和調(diào)用,這種方式會(huì)改變函數(shù)的內(nèi)部名字。此處如果把"C"都去掉也可以,但C程序?qū)o法調(diào)用。另外,使用GetProcAddress函數(shù)時(shí)也會(huì)發(fā)生困難,因?yàn)?br>編譯程序已經(jīng)把函數(shù)名字改變了,無法用原來的名字得到函數(shù)地址。(核心編程說的不明白,沒想到這本書錯(cuò)誤這么多)
發(fā)行的時(shí)候,將頭文件、.lib文件和DLL文件給用戶就可以了。lib文件的作用是說明了頭文件中函數(shù)所在的DLL文件,如果沒有l(wèi)ib文件,編譯器將在鏈接過程中提示錯(cuò)誤:unresolved external symbol 函數(shù)名。
事實(shí)上,調(diào)用DLL有兩種方式,第一種是比較常用,即包含DLL的頭文件,并在鏈接的時(shí)候?qū)?dòng)態(tài)鏈接庫(kù)同exe文件像連接,建立輸入表。這個(gè)時(shí)候需要.lib文件。第二種方法exe文件中沒有輸入表,程序使用LoadLibrary(Ex)和GetProcAddress()顯式的加載DLL文件(卸載用FreeLibrary()),這個(gè)時(shí)候不需要.lib文件。
HINSTANCE LoadLibrary(PCTSTR pszDLLpathName);
HINSTANCE LoadLibraryEx(PCTSTR pszDLLpathName,NULL,0);

兩次調(diào)用LoardLibrary并不會(huì)裝載兩次dll文件,只是將dll映射進(jìn)進(jìn)程的地址空間。系統(tǒng)會(huì)自動(dòng)為每個(gè)進(jìn)程維護(hù)一個(gè)dll的計(jì)數(shù)。FreeLiabray會(huì)使計(jì)數(shù)減一,如果計(jì)數(shù)為0,系統(tǒng)就會(huì)將dll從進(jìn)程的地址空間卸載。


HINSTANCE GetModuleHandle(PCTSTR pszModuleName);//確定dll是否已經(jīng)被映射進(jìn)地址空間。
HINSTANCE hinstDll=GetModuleHandle("MyLib");
if(hinstDll==NULL)
{
    hinstDll=LoadLibrary("MyLib");
}

DWORD GetModuleFileName(
    HINSTANCE hinstModule,
    PTSTR pszPathName,
    DWORD cchPath
}
可以獲得某個(gè)模塊(.exe或者dll)的全路徑名。

幾個(gè)函數(shù)的用法:(注意GetProcAddress()函數(shù)的用法,如何定義和使用一個(gè)函數(shù)指針)
typedef int (*MYPROC)(int,int);

int main()
{
 HINSTANCE t;
 t=LoadLibraryEx(TEXT("tt.dll"),NULL,0);
 if(t)
 {
  cout<<TEXT("load success")<<endl;

 }
 HINSTANCE hinstDll=GetModuleHandle("tt.dll");
 if(hinstDll==NULL)
 {
  cout<<TEXT("first load failed")<<endl;
  hinstDll=LoadLibrary("MyLib");
 }
 size_t sz=100;
 PTCHAR str=new TCHAR[sz];
 GetModuleFileName(t,str,sz);
 cout<<str<<endl;
 delete str;
 MYPROC add=NULL;
 add=(MYPROC)GetProcAddress(t,"add");
    if(NULL!=add)
    {
  cout<<(*add)(1,2)<<endl;
 }
 FreeLibrary(t);

 return 0;
}


UNICODE
ANSI/UNICODE通用的定義方法(轉(zhuǎn)換只需要在編譯的時(shí)候使用_UNICODE和UNICODE):
TCHAR _TEXT("success") PTSTR PCTSTR _tcscpy(),_tcscat();
使用BYTE PBYTE定義字節(jié),字節(jié)指針和數(shù)據(jù)緩沖。
傳遞給函數(shù)的緩存大小:sizeof(szBuffer)/sizeof(TCHAR)
給字符串分配內(nèi)存:malloc(nCharacters*sizeof(TCHAR));
其它的字符串函數(shù):
PTSTR CharLower(PTSTR pszString);
PTSTR CharUpper(PTSTR pszString);
轉(zhuǎn)換單個(gè)字符:
TCHAR c=CharLower((PTSTR)szString[0]);
轉(zhuǎn)換緩存中的字符串(不必以0結(jié)尾):
DWORD CharLowerBuff(
PTSTR pszString,
DWORD cchString);
DWORD CharUpperBuff(
PTSTR pszString,
DWORD cchString);

BOOL IsCharAlpha(TCHAR ch);
BOOL IsCharAlpahNumeric(TCHAR ch);
BOOL IsCharLower(TCHAR ch);
BOOL IsCharUpper(TCHAR ch);

線程本地存儲(chǔ)(TLS):為進(jìn)程的每個(gè)線程存儲(chǔ)私有數(shù)據(jù)。用于那些一次傳遞參數(shù)后多次調(diào)用的函數(shù)(函數(shù)會(huì)保存上次調(diào)用的數(shù)據(jù))。
實(shí)現(xiàn)方法:進(jìn)程中有一個(gè)位標(biāo)志樹組(win2000的這個(gè)數(shù)組大小超過1000)。在每個(gè)線程中有一個(gè)對(duì)應(yīng)的PVOID數(shù)組。通過設(shè)定位標(biāo)志樹組的某個(gè)位來分配每個(gè)線程中的PVOID數(shù)組得相應(yīng)單元。函數(shù)需要每次檢索線程的PVOID數(shù)組,獲得該線程的相應(yīng)數(shù)據(jù)。

DWORD TlsAlloc();  //為每個(gè)線程分配一個(gè)空的PVOID數(shù)組單元。

BOOL TlsSetValue(  //線程設(shè)定自己的PVOID數(shù)組單元。
DWORD dwTlsIndex,
PVOID pvTlsValue);

PVOID TlsGetValue(
DWORD dwTlsIndex);  //檢索PVOID數(shù)組。

BOOL TLSFree(
DWORD dwTlsIndex);  //釋放PVOID數(shù)組單元

靜態(tài)TLS:__declspec(thread) DWORD gt_dwStartTime=0;//只能修飾全局或者靜態(tài)變量。

DLL掛接(進(jìn)程注入):讓自己的DLL插入到其他進(jìn)程的地址空間。

1 使用注冊(cè)表插入DLL
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
將你的DLL路徑放入這個(gè)關(guān)鍵字下面。當(dāng)User32.dll被映射到進(jìn)程中的時(shí)候,它會(huì)加在這個(gè)關(guān)鍵字下的每個(gè)庫(kù)。
注意:(1)對(duì)win98無效
      (2)由于加載事間比較早,你的DLL可能無法調(diào)用kernel32以外的dll.
       (3) 如果進(jìn)程沒有使用user32.dll,這個(gè)方法無效。
       (4) 需要重新啟動(dòng)。
      (5)user32不會(huì)檢查每個(gè)庫(kù)是否加載成功。
2 使用windows鉤子
HOOK hHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hinstDll,0);
BOOL UnhookWindowsHookEx(HHOOK hhook);
具體過程如下:
      (1)進(jìn)程B的一個(gè)線程準(zhǔn)備發(fā)送消息給一個(gè)窗口。
      (2)系統(tǒng)察看線程上是否已經(jīng)安裝了WH_GETMESSAGE鉤子。
      (3)系統(tǒng)察看GetMsgProc的DLL是否已經(jīng)映射到了進(jìn)程B的地址空間,如果沒有,系統(tǒng)將把DLL映射到B的地址空間,并自動(dòng)增加引用計(jì)數(shù)。
      (4)調(diào)用GetMsgProc函數(shù),返回時(shí),系統(tǒng)會(huì)自動(dòng)為DLL的引用計(jì)數(shù)減一。

3 使用遠(yuǎn)程線程來插入DLL
      (1)使用VirtualAllocEx,分配遠(yuǎn)程進(jìn)程的地址空間的內(nèi)存。
      (2)使用WriteProcessMemory,將Dll的路徑名拷貝到第一個(gè)步驟中已經(jīng)分配的內(nèi)存中。
      (3)使用GetProcAddress,獲得LoadLibrary的實(shí)際地址。
      (4)使用CreateRemoteThread,在遠(yuǎn)程進(jìn)程中創(chuàng)建一個(gè)線程。
      退出: 
      (5)使用VirtualFreeEx,釋放內(nèi)存
      (6)使用GetProcAddress,獲得FreeLiabary的地址。
      (7)使用CreateRemoteThread,在遠(yuǎn)程進(jìn)程中創(chuàng)建一個(gè)線程,調(diào)用FreeLiabary函數(shù)。
4 使用特洛伊DLL插入
  替換dll.
5 將DLL作為調(diào)試程序插入
6 win98內(nèi)存映射文件,creatprocess

結(jié)構(gòu)化異常處理:
結(jié)束處理程序:__try{} __finally{}
除非__try執(zhí)行中進(jìn)程或者線程結(jié)束,否則總會(huì)執(zhí)行__finally,并且__finally中的return會(huì)替代__try中的return;好的習(xí)慣是將return ,continue,break,goto語(yǔ)句拿到結(jié)構(gòu)化異常處理語(yǔ)句外面,可以節(jié)省開銷。將__try中的return 換成__leave,可以節(jié)省開銷。在__finally總確定時(shí)正常進(jìn)入還是展開進(jìn)入:
BOOL AbnormalTermination();//正常進(jìn)入返回FALSE,局部展開或者全局展開返回TRUE;

異常處理程序:__try{}__exception(異常過濾表達(dá)式){}

EXCEPTION_EXECUTE_HANDLE
表示處理異常,處理后轉(zhuǎn)到exception塊后面的代碼繼續(xù)執(zhí)行。
EXCEPTION_CONTINUE_EXECUTION
EXCEPTION_CONTINUE_SEARCH

可以對(duì)異常過濾表達(dá)式進(jìn)行硬編碼,也可以用一個(gè)調(diào)用一個(gè)函數(shù)來決定過濾表達(dá)式,函數(shù)的返回值是LONG.例如,可以進(jìn)行一定處理,然后返回EXCEPTION_CONTINUE_EXECUTION,再次執(zhí)行出錯(cuò)語(yǔ)句。但可能再次出錯(cuò),因此這種方法必須小心,防止生成死循環(huán)。

DWORD GetExceptionCode() 可以獲得異常種類。它只能在__except后的括號(hào)或者異常處理程序中調(diào)用。

發(fā)生異常后,操作系統(tǒng)會(huì)像引起異常的線程的棧里壓入三個(gè)結(jié)構(gòu):EXCEPTION_RECORD CONTEXT EXCEPTION_POINTERS,其中第三個(gè)結(jié)構(gòu)包含兩個(gè)成員指針,分別指向前兩個(gè)結(jié)構(gòu),使用函數(shù)可以獲得第三個(gè)結(jié)構(gòu)的指針:
PEXCEPTION_POINTERS GetExceptionInformation();//僅可以在異常過濾器中調(diào)用,既__exception后面的小括號(hào)。

逗號(hào)表達(dá)式:從左到右對(duì)所有的表達(dá)式求值,并返回最有面的表達(dá)式的值。

引發(fā)軟件異常:
VOID RaiseException(
DWORD dwExceptionCode,
DWORD dwExceptionFlags,
DWORD nNumberOfArguments,
CONST ULONG_PTR *pArguments);

缺省調(diào)試器所在注冊(cè)表:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug  Debugger
win98是存放在win.ini里

調(diào)試器掛接到被調(diào)試進(jìn)程
BOOL DebugActiveProcess(DWORD dwProcessID);


while(*str++!='\0');
應(yīng)該注意的是在不滿足條件后,str仍然會(huì)自加1。

位操作符>>和<<不會(huì)做循環(huán)位移,即不會(huì)把移出的位放到另一頭。

多態(tài)性和動(dòng)態(tài)聯(lián)編的實(shí)現(xiàn)過程分析

  一、基礎(chǔ):

  1、多態(tài)性:使用基礎(chǔ)類的指針動(dòng)態(tài)調(diào)用其派生類中函數(shù)的特性。

  2、動(dòng)態(tài)聯(lián)編:在運(yùn)行階段,才將函數(shù)的調(diào)用與對(duì)應(yīng)的函數(shù)體進(jìn)行連接的方式,又叫運(yùn)行時(shí)聯(lián)編或晚捆綁。

  二、過程描述:

  1、編譯器發(fā)現(xiàn)一個(gè)類中有虛函數(shù),編譯器會(huì)立即為此類生成虛擬函數(shù)表 vtable(后面有對(duì)vtable的分析)。虛擬函數(shù)表的各表項(xiàng)為指向?qū)?yīng)虛擬函數(shù)的指針。

  2、編譯器在此類中隱含插入一個(gè)指針vptr(對(duì)vc編譯器來說,它插在類的第一個(gè)位置上)。

  有一個(gè)辦法可以讓你感知這個(gè)隱含指針的存在,雖然你不能在類中直接看到它,但你可以比較一下含有虛擬函數(shù)時(shí)的類的尺寸和沒有虛擬函數(shù)時(shí)的類的尺寸,你能夠發(fā)現(xiàn),這個(gè)指針確實(shí)存在。

  3、在調(diào)用此類的構(gòu)造函數(shù)時(shí),在類的構(gòu)造函數(shù)中,編譯器會(huì)隱含執(zhí)行vptr與vtable的關(guān)聯(lián)代碼,將vptr指向?qū)?yīng)的vtable。這就將類與此類的vtable聯(lián)系了起來。

  4、在調(diào)用類的構(gòu)造函數(shù)時(shí),指向基礎(chǔ)類的指針此時(shí)已經(jīng)變成指向具體的類的this指針,這樣依靠此this指針即可得到正確的vtable,從而實(shí)現(xiàn)了多態(tài)性。在此時(shí)才能真正與函數(shù)體進(jìn)行連接,這就是動(dòng)態(tài)聯(lián)編。

定義純虛函數(shù)方法:virtual returntype function()= 0;

所有的類型都可以用new動(dòng)態(tài)創(chuàng)建,包括類,結(jié)構(gòu),內(nèi)置數(shù)據(jù)類型。

exit()函數(shù)包含在"cstdlib"中

泛型編程,使用STL庫(kù)
總共有近75個(gè)泛型算法

所有容器的共通操作:
== != = empty() size() clear(),begin(),end(),以及insert和erase,不過后者隨容器的的不同而不同

序列式容器:
vector(數(shù)組):插入和刪除的效率較低,但存取效率高。
list(雙向鏈表):與前者相反,插入和刪除的效率較高,但存取效率低。每個(gè)元素包含三個(gè)字段:value back front.
deque(隊(duì)列):在前端和末尾操作效率高。

生成序列式容器的五種方法:
1 產(chǎn)生空的容器:
list<string> slist;
vector<int> vtor;
2 產(chǎn)生特定大小的容器,容器中的每個(gè)元素都以其默認(rèn)值為初值(發(fā)現(xiàn)VC中的int double等沒有默認(rèn)值)。
list<int> ilist(1024);
vector<string> svec(24);
3 產(chǎn)生特定大小的容器,并為每個(gè)元素指定初值:
list<int ilist(1024,0);
vector<string> svec(24,"default");
4 通過一對(duì)迭代器產(chǎn)生容器,這對(duì)迭代器用來表示數(shù)組作為初值的區(qū)間:
int ia[10]={1,2,3,4,5,6,7,8,9,0};
vector<int>  iv(ia+2,ia+8);
5 復(fù)制某個(gè)現(xiàn)有的容器的值:
vector<int> ivec1;
//填充ivec1;
vector<int> ivec2(ivec1);

有6個(gè)方法用于操作開始和末尾的元素:push_front() pop_front() push_back() pop_back(),由于pop操作僅刪除元素而不返回元素,因此還需要front() back()方法取開始和末尾的元素,另外,vector不包括push_front() pop_front方法,很顯然,無法實(shí)現(xiàn)。

intert的四種變形:
iterator insert(iterator position,elemType value):將value插到position前。返回值指向被插入的元素。
void insert(iterator position,int count,elemType value):在position前插入count個(gè)元素,每個(gè)元素都是value.
void insert(iterator1 position,iterator2 first,iterator2 last):將first,last之間的元素插到position前.
iterator insert( iterator position):在position前插入元素,初值為所屬類型的默認(rèn)值。

erase的兩種變形:
1  iterator erase(iterator posit):刪除posit指向的元素。
list<string>::iterator it=find(slist.begin(),slist,end(),str);
slist.erase(it);
2  iterator erase(iterator first,iterator last):刪除first,last間的元素。

list不支持iterator的偏移運(yùn)算

對(duì)于常值容器,使用常迭代器:
const vector<string> cs_vec;
vector<string::const_iterator iter_cs_vec.begin();

iterator可以當(dāng)作指針用,可以用*取內(nèi)容,也可以用->調(diào)用對(duì)象的成員。

使用泛型算法
#i nclude <algorithm>

find():線性搜索無序集合
binary_search():二分搜索有序集合。
count():返回元素個(gè)數(shù)。
search():搜索序列,如果存在返回的iterator指向序列首部,否則指向容器末尾。
max_element(begin,end):返回區(qū)間內(nèi)的最大值。
copy(begin,end,begin):元素復(fù)制。
sort(begin,end):排序。

function objects:#i nclude <functional>
算術(shù)運(yùn)算:
plus<type> minus<type> negate<type> multiplies<type> divides<type> modules<type>
關(guān)系運(yùn)算:
less<type> less equal<type> greater<type greater equal<type> equal_to<type> not_equal_to<type>
邏輯運(yùn)算:
logical_and<type> logical_or<type> logical_not<type>

adapter:適配器。
bind1st:將數(shù)值綁定到function object的第一個(gè)參數(shù)。
bind2nd:將數(shù)值綁定到function object的第二個(gè)參數(shù)。

使用map:
#i nclude <map>
#i nclude <string>
map<string,int> words;

words["vermeer"]=1;

map<string,int>::iterator it=words.begin();
for(;it!=words.end();++it)
cout<<"key:"<<it->first<<"value:"<<it->second<<endl;

查找map元素的方法:
words.find("vermeer");//返回iterator,指向找到的元素,找不到返回end();
還可以:
if(words.count(search_word))
count=words[search_word];

使用set:
#i nclude <set>
#i nclude <string>
set<string> word_exclusion;
//判斷是否存在某個(gè)元素
if(word_exclusion.count(tword))
//默認(rèn)情況下,所有元素按less-than運(yùn)算排列

//加入元素
iset.insert(ival);
iset.insert(vec.begin(),vec.end());

與set相關(guān)的算法
set_intersection() set_union() set_difference() set_symmetric_difference()

使用insertion adapters:
#i nclude <iterator>
back_inserter()
inserter()
front_inserter()

使用STL通常會(huì)有很多警告,為了避免在調(diào)試模式(debug mode)出現(xiàn)惱人的警告,使用下面的編譯器命令:

#pragma warning(disable: 4786)

strncpy(dest,source,count) if(count〈=strlen(source)),那么null結(jié)尾不會(huì)被加在dest的尾部,如果count>strlen(source),那么不足的部分會(huì)用null填充。


windows內(nèi)存是由高地址向底地址分配的,但變量的存儲(chǔ)是從底地址到高地址的,如INT類型的四個(gè)字節(jié),數(shù)組的每個(gè)元素。
 
內(nèi)存復(fù)制的時(shí)候不能用字符串拷貝函數(shù),因?yàn)榧词故褂胹trncpy指定了復(fù)制的長(zhǎng)度,拷貝函數(shù)也會(huì)遇到'\0'自動(dòng)終止,要使用MEMSET。

由于對(duì)齊的關(guān)系,下面兩個(gè)結(jié)構(gòu)使用sizeof,前者是12,后者是16。
struct DNSAnswer
{
 unsigned short name;
 unsigned short type;
 unsigned short cla;
 unsigned short length;
 unsigned int   ttl;
};
struct DNSAnswer
{
 unsigned short name;
 unsigned short type;
 unsigned short cla;
 unsigned int   ttl;
        unsigned short length;
};

子類可以使用父類的保護(hù)成員,而友元比子類的權(quán)限還大,可以使用類的私有和保護(hù)成員。

在內(nèi)存分配失敗的情況下,系統(tǒng)只有在出錯(cuò)處理函數(shù)為空的情況下,才會(huì)拋出異常:std::bad_alloc(),否則會(huì)反復(fù)調(diào)用處理函數(shù)并再次嘗試分配內(nèi)存。

如果重載了NEW,那么在繼承的時(shí)候要小心,如果子類沒有覆蓋NEW,那么它會(huì)去使用父類的NEW ,因此應(yīng)該在new,delete中做檢查
if (size != sizeof(base))             // 如果數(shù)量“錯(cuò)誤”,讓標(biāo)準(zhǔn)operator new,base為類名
    return ::operator new(size);        // 去處理這個(gè)請(qǐng)求

  if (size != sizeof(base)) {      // 如果size"錯(cuò)誤",
    ::operator delete(rawmemory);  // 讓標(biāo)準(zhǔn)operator來處理請(qǐng)求
    return;                       
  }

c++標(biāo)準(zhǔn)規(guī)定,要支持0內(nèi)存請(qǐng)求(分配一個(gè)字節(jié)),并且可以刪除NULL指針(直接返回)。

在創(chuàng)建線程的時(shí)候,傳遞的變量一定要是全局或者靜態(tài)的變量,因?yàn)閭鬟f的是變量的地址,如果是局部變量地址很快就會(huì)失效。

主線程退出后,其子線程自動(dòng)結(jié)束。

智能指針:它可以避免內(nèi)存泄露,因?yàn)橹悄苤羔樖窃跅I蟿?chuàng)建的;還可以避免堆上內(nèi)存的重復(fù)釋放錯(cuò)誤,因?yàn)樗WC只有一個(gè)指針擁有這塊內(nèi)存的所有權(quán)。

posted on 2007-08-13 09:22 Xiao.Zhu 閱讀(1252) 評(píng)論(0)  編輯 收藏 引用

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美sm重口味系列视频在线观看| 欧美日本国产精品| 欧美国产精品| 欧美成人第一页| 欧美成人影音| 亚洲精品一二区| 亚洲精品国久久99热| 蜜桃伊人久久| 亚洲人成久久| 亚洲男人的天堂在线| 亚洲综合精品四区| 久久一本综合频道| 亚洲第一搞黄网站| 老鸭窝毛片一区二区三区| 欧美**人妖| 欧美性猛交xxxx乱大交蜜桃| 国产精品一区在线观看| 黄色工厂这里只有精品| 亚洲精选一区二区| 欧美亚洲一区| 欧美国产一区在线| 亚洲一区视频在线| 欧美不卡福利| 国模套图日韩精品一区二区| 一区二区免费在线播放| 可以看av的网站久久看| 宅男噜噜噜66一区二区| 久久久一区二区| 国产精品久久久久久久久久三级| 在线观看三级视频欧美| 亚洲永久免费视频| 你懂的国产精品| 午夜在线电影亚洲一区| 欧美人妖在线观看| 亚洲黄色高清| 久久人人97超碰精品888| 一区二区不卡在线视频 午夜欧美不卡在 | 91久久久久久久久| 亚洲专区一区| 欧美日韩91| 亚洲国产高潮在线观看| 午夜精品视频在线观看| 亚洲国产日韩在线一区模特| 久久不射2019中文字幕| 国产精品久久福利| 日韩视频亚洲视频| 欧美成人官网二区| 久久久久国产免费免费| 国产伦精品一区二区| 亚洲午夜国产一区99re久久| 欧美成人在线网站| 久久精品国产免费看久久精品| 欧美午夜精品久久久久久超碰| 91久久精品www人人做人人爽| 久久国产精品一区二区三区四区 | 午夜精品久久久久久久99樱桃 | 午夜精品久久99蜜桃的功能介绍| 国产精品拍天天在线| 亚洲精品日韩在线观看| 亚洲一区视频在线| 亚洲精品免费在线播放| 欧美国产日韩xxxxx| 欧美午夜不卡| 欧美有码在线视频| 另类欧美日韩国产在线| 亚洲精品视频啊美女在线直播| 久久字幕精品一区| 欧美日韩国产123区| 亚洲无线视频| 国产精品盗摄一区二区三区| 性做久久久久久免费观看欧美| 欧美 日韩 国产一区二区在线视频| 亚洲欧美日韩精品久久亚洲区| 免费久久99精品国产自| 亚洲二区免费| 欧美激情中文字幕在线| 久久久水蜜桃| 亚洲男人的天堂在线aⅴ视频| 久久嫩草精品久久久精品一| 亚洲视频欧美视频| 欧美大片在线观看一区| 精品成人在线| 亚洲高清在线观看| 欧美第十八页| 国产一区二区高清不卡| 亚洲国产精选| 久久成人免费网| 欧美淫片网站| 久久亚洲影音av资源网| 亚洲激情视频| 尤物九九久久国产精品的特点| 久久精品国产99国产精品| 欧美视频在线播放| 亚洲一区二区在线播放| 黄网站免费久久| 伊人成人在线视频| 在线观看欧美视频| 亚洲国产精品va在线看黑人动漫 | 欧美有码在线观看视频| 久久久综合精品| 一区二区欧美在线| 亚洲一级特黄| 亚洲国产欧美日韩| 亚洲特色特黄| 亚洲成人资源| 亚洲一区三区电影在线观看| 亚洲黄色影片| 欧美jizz19hd性欧美| 午夜精品久久久| 一区二区三区视频在线| 久久福利精品| 午夜国产精品视频| 欧美成年人视频网站| 欧美一乱一性一交一视频| 欧美成人激情视频免费观看| 欧美在线视频观看| 欧美日韩国产精品一区| 美女网站久久| 国产精品亚洲人在线观看| 亚洲丰满少妇videoshd| 韩国成人福利片在线播放| 亚洲一级片在线观看| 中国女人久久久| 女仆av观看一区| 久久先锋影音| 国产精品婷婷| 99热精品在线观看| 亚洲精品欧美日韩专区| 久久久一区二区三区| 久久黄色网页| 国产精品日韩专区| 亚洲天堂av在线免费观看| 一本一本久久| 牛牛精品成人免费视频| 蜜桃久久精品乱码一区二区| 国产无遮挡一区二区三区毛片日本| 亚洲精品社区| 日韩一级二级三级| 欧美激情久久久久| 亚洲福利视频二区| 亚洲三级国产| 欧美极品一区| 亚洲国产一区二区三区在线播| 亚洲电影在线播放| 久久久精品国产免大香伊| 久久精品国亚洲| 国产欧美日韩精品一区| 亚洲欧美日韩在线高清直播| 亚洲影院污污.| 国产精品国内视频| 亚洲一区图片| 欧美一区二区黄| 国产午夜精品久久| 久久国产精品久久久久久电车 | 香蕉久久夜色精品国产使用方法 | 国产曰批免费观看久久久| 羞羞色国产精品| 久久婷婷一区| 亚洲日本理论电影| 欧美日韩免费一区| 亚洲午夜激情免费视频| 欧美在线啊v| 精品二区视频| 蜜臀va亚洲va欧美va天堂| 欧美顶级少妇做爰| 日韩视频二区| 国产伦精品一区二区三区四区免费| 亚洲综合二区| 免费欧美在线| 制服诱惑一区二区| 狠狠色狠狠色综合日日五| 免费黄网站欧美| 一区二区日韩欧美| 久久精品在线观看| 亚洲精品一区二区三区四区高清 | 久久亚裔精品欧美| 亚洲日本中文字幕区| 欧美三级日本三级少妇99| 欧美在现视频| 亚洲精品国产拍免费91在线| 小嫩嫩精品导航| 亚洲国产91精品在线观看| 欧美日韩在线精品一区二区三区| 中文网丁香综合网| 免费亚洲一区二区| 亚洲欧美在线看| 欧美猛交免费看| 欧美一级专区| 日韩视频在线免费观看| 久久久欧美精品sm网站| 亚洲午夜激情免费视频| 激情婷婷亚洲| 欧美午夜视频| 欧美国产乱视频| 老司机凹凸av亚洲导航| 午夜在线视频一区二区区别| 日韩视频―中文字幕| 亚洲经典三级| 亚洲国产精品一区二区第四页av | 欧美亚洲一区在线|