孫東風:聯網游戲、應用開發方面有獨特的優勢!
并不完全算是我的原創,只是在原來文章基礎上加上了一點自己的理解.
1.結構大小
int *pi = new int[12];
中的pi純粹是個指針變量,它就是一個指針,在32位環境下占4個字節。
pi申請完內存后,是整個內存塊的首地址,*pi代表的就是第一個元素,每個元素為int類型的,當然為4了,如果為char *pi = new char[12];
cout<<"*pi:"<
int ia[]={0, 1, 2};
中的ia是個數組,它代表了整個這一個數組,數組中有三個元素,每個int類型的元素占4 個字節,共是12個字節。但是ia是個變量,變量總要有一定的值,C++就把數組第一個元素的地址賦給它了。
#include
//using namespace std;
int main(int argc, _TCHAR* argv[])
{
int *pi = new int[12];
int ai[] = {0,1,2};
std::cout<<"*pi :"<
std::cout<<"ai :"<
return 0;
}
2.指針與引用
引用是一種沒有指針語法的指針.與指針一樣,引用提供對對象的間接訪問.
傳指針時,我們可以通過指針來修改它在外部所指向的內容。但如果要修改外部指針讓它指向別的對象是不可能的。
例如傳遞外部指針到函數內來分配空間,必須傳遞指針的指針或指針的引用。
3. 引用與指針的比較:
①引用在創建的同時必須初始化,即引用到一個有效的對象(因為引用原本就是對象的一個別名);
而指針在定義的時候不必初始化,可以在定義后面的任何地方重新賦值.
②不存在NULL引用,引用必須與合法的存儲單元關聯;
而指針則可以是NULL.
③引用一旦被初始化為指向一個對象,它就不能被改變為另一個對象的引用;
而指針在任何時候都可以改變為指向另一個對象.
給引用賦值并不是改變它和原始對象的綁定關系.
④引用的創建和銷毀并不會調用類的拷貝構造函數;
⑤在語言層面,引用的用法和對象一樣;
在二進制層面,引用一般都是通過指針來實現的,只不過編譯器幫我們完成了轉換.
總的來說:引用既具有指針的效率,又具有變量使用的方便性和直觀性.
1:值傳遞
void Func(int x)
{
x=x+10;//修改的是n在堆棧中的拷貝x
}
int n=0;
Func(n);
cout<<"n="<
2:指針傳遞
void Func2(int *x)
{
(*x)=(*x)+10;//修改指針x指向的內存單元的值
}
int n=0;
Func(&n);
cout<<"n="<
3:引用傳遞
void Func3(int &x)
{
x=x+10;//修改的是x引用到的對象n
}
int n=0;
Func3(n);
cout<<"n="<
4. 函數指針
在注冊一個回調函數時候,我們常常使用函數指針.
c/c++的連接器在連接程序的時候必須把函數的調用語句加上,因此函數地址必須在編譯時就確定下來.也就是編譯器為函數生成代碼的時候.
typedef int(* FuncPtr) (const char*)//定義一個函數指針的類型
類的成員函數有4種類型:inline,virtual,static,normal;
inline函數在運行時會展開,雖然語言允許取其地址,但是沒有太大的意義.
virtual成員函數的地址指的是其在vtable中的位置.
static成員函數的地址和普通全局函數的地址沒有任何區別.
普通成員函數的地址和一般函數的地址也沒有區別,就是函數代碼在內存中的真實地址,但是由于它的調用要綁定到一個實實在在的對象身上,因此無論是其函數指針的聲明方式還是其地址的獲取方式都比較特別.
#i nclude
class CTest
{
public:
void f(void) //普通成員函數
{
cout<<"CTest::f()"<
}
static void g(void) //靜態成員函數
{
cout<<"CTest::g()"<
}
virtual void h(void) //虛擬的成員函數
{
cout<<"CTest::h()"<
}
private:
};
void main()
{
typedef void (*GFPtr) (void); //定義一個全局函數指針類型
GFPtr fp=CTest::g; //取靜態成員函數地址的方法和取一個全局函數的地址相似
fp(); //通過函數指針調用類靜態成員函數
typedef void (CTest::*MemFuncPtr)(void); //聲明類成員函數指針類型
MemFuncPtr mfp_1=&CTest::f; //聲明成員函數指針變量并初始化
MemFuncPtr mfp_2=&CTest::h; //注意獲得成員函數地址的方法
CTest theObj;
(theObj.*mfp_1)(); //使用對象和成員函數指針調用成員函數
(theObj.*mfp_2)();
CTest *pTest=&theObj; //使用對象指針和成員函數指針調用成員函數
(pTest->*mfp_1)();
(pTest->*mfp_2)();
}
實際上,任何成員函數的代碼體都是獨立于類的對象而存在的,只是非靜態成員函數在調用的時候需要與具體的對象建立綁定關系而已(即this指針),c/c++編譯器最終把所有的成員函數經過Name-Mangling的處理后轉換全局函數,并且增加一個入參this作為第一個參數,供所屬類的所有對象共享.因此成員函數的地址實際上就是這些全局函數的地址.
5. 內存管理
內存分配的方式:
(1)從靜態存儲區域分配.內存在程序的時候就已經分配好了(即已經編址),這些內存在程序的整個運行期間都存在.例如全局變量,static變量度等.
(2)在堆棧上創建.在函數執行期間,函數內局部變量(包括形參)的存儲單元都創建在堆棧上,函數結束時這些存儲單元自動釋放(堆棧清棧).堆棧內存分配運算內置于處理器的指令集中,效率很高,并且不存在失敗的危險,但是分配的內存容量有限.
(3)從堆上分配,亦稱動態內存分配.程序在運行期間用malloc 或new 申請任意數量的內存,程序員自己掌握釋放內存的恰當時機(使用free或delete).動態內存的生存期由程序員決定,使用非常靈活,但也最容易產生問題.
有了malloc/free為什么還要new/delete?
malloc與free是c/c++ 語言的標準庫函數,new/delete是c++的運算符,它們都是用于申請和釋放動態內存.
對于非內部的數據類型(如ADT/UDT)的對象而言,光用malloc/free無法滿足動態對象的要求:對象在創建的同時要自動調用構造函數,對象在銷毀的時候要自動調用析構函數.由于malloc/free是庫函數,不在編譯器控制范圍內.
malloc/free的使用要點:
函數malloc的原型如下
void *malloc(size_t size);
用malloc申請一塊長度為length的整型數組的內存,程序如下
int *p=(int*)malloc(length);
函數free的原型如下一步
void free(void* memblock);
如果p==NULL,free對p的操作無論多次都不會出現問題.
new/delete的使用要點:
int *p1=(int*)malloc(sizeof(int)*length);
int* p2=new int[length];
note:如果用new 創建對象數組,那么只能使用對象的默認構造函數,不能使用帶參數的構造函數.
在使用delete釋放對象數組時,留意不要丟了符號"[]"
對于內部數據類型(int,float,double)的動態數組p而言,delete p與delete[]p是等價的.
全寫法Cache(Write through)
每次寫入Cache時同時寫入內存,使內存和Cache始終保持一致.
寫回法Cache(Write back)
當每一次寫入時,只對Cache寫入,如果已經寫入過的Cache需要再寫入時,就要把它的內容寫入內存中