首先請看如下代碼:
#include <stdio.h>
int getLength(char a[]);
int main()
{
char a[9]="123456789";
char b[10]="123456789";
char *b="123456789";
printf("sizeof a[]:%d/n",sizeof(a)); /* return the length of array a,including '/0'*/
printf("After the function getLength() was called:%d/n",getLength(a));
printf("sizeof b:%d/n",sizeof(b)); /* return the length of pointer which type is int*/
return 0;
}
int getLength(char a[])
{
return sizeof(a);
}
這段代碼將引起 編譯錯誤,錯誤資訊如下:
test_5.cpp(7) : error C2117: '123456789' : array bounds overflow
數組邊界溢出這個不用做過多解釋了吧。
我們將char a[9]="123456789";注釋掉,繼續編譯。程式通過連編,結果如下:
sizeof a[]:10
After the function getLength() was called:4
sizeof b:4
這次我們發現同樣是sizeof,通過函數傳入參數和直接使用該參數,結果是不一樣的。那麼肯定是函數調用的過程中發生了什麼。到底發生了什麼呢?我也說不清楚,但是肯定有人能說清楚,=_=b
然後,我們對該程式進行調試,在我的機器上(以下測試均在我的機器上,你們可能會有不同結果),a的地址為0x12ff74,b的地址為0x00422fc,他們的內容為字串123456789,注意:在9後面還有一個'/0'。
我們再在程式char *b="123456789";後面添加一句:char *c="123456789";然後再調試該程式,我們發現c的地址為0x00422fc,和b指向同一個地址。
我們再往語句char a[9]="123456789";的前面添加2行:int i,j;繼續調試,我們發現i的地址為0x0012ff7c,j的地址為0x0012ff78。i和j均為局部變數,應該在棧上分配記憶體,按照順序,先分配i的記憶體,然後分配j的記憶體,而i的地址大於j的地址,所以我們可以得出棧是從高地址到低地址的,也就是棧的底部在高地址端,棧的方向是向下的。
我們繼續新增內容,在程式的開始加入:int *k;在return 0;之前加入k=new int(10);,這時我們可以通過調試發現執行k=new int(10);之前k的值是0xcccccccc,所有指標在初始化之前都是這個值(僅限這次測試的機器)。執行了k=new int(10);之後,k的值為0x00371000。由於new一般是在堆上分配記憶體,所以我們可以猜測堆就在0x00371000這附近,然後再繼續加入語句:int *l;以及l=new int(20);調試發現l的地址為0x00371048。再繼續加入語句:int *m;以及m=new(30);調試發現地址為0x00371090,這裡我們能找到的規律就是越往後分配的記憶體其地址越大,所以它的方向是向上的,而且每次每次分配的記憶體塊大小都是0x48位元組。這樣我們得出結論:動態分配記憶體按塊分配,而且方向由低地址向高地址。
關於C++友元。我們需要記住的是友元不能是虛函數,因為友元不是類成員,而只有成員才能是虛函數。如果由於這個原因引起了設計問題,可以通過讓友元函數調用虛擬成員函數來解決。
沒有重新定義。假設建立了如下所示的代碼:
class Dwelling
{
public:
virtual void showperks(int a) const;
};
void Dwelling::showperks(int a) const
{}
class Hovel:public Dwelling
{
public:
virtual void showperks() const;
};
void Hovel::showperks() const
{}
int main()
{
Hovel trump;
trump.showperks();
return 0;
}
但是調用trump.showperks(5);則發生錯誤。這說明在子類中重新定義基類函數不會產生函數的兩個重載版本,而是隱藏了接受一個int參數的基類版本。簡而言之,重新定義繼承的方法並不是重載。
這引出了兩條經驗規則:第一,如果重新定義繼承的方法,應確保與原來的原型完全相同,但如果傳回型別是基類引用或指標,則可以修改為指向衍生類別的引用或指標(這種例外是新出現的),但是VC 6好像不支援。
第二,如果基類聲明被重載了,則應在衍生類別中重新定義所有的基類版本。
單件模式(Singleton)。代碼如下。
#include <iostream>
using namespace std;
class TheOnlyInstance
{
public:
static TheOnlyInstance* GetTheOnlyInstance();
void OtherMethod(){cout<<"other method called./n";}
protected:
TheOnlyInstance(){}
private:
static TheOnlyInstance *instance;
};
TheOnlyInstance * TheOnlyInstance::instance=NULL;
TheOnlyInstance* TheOnlyInstance::GetTheOnlyInstance()
{
if(instance==NULL)
{
instance=new TheOnlyInstance;
return instance;
}
else return instance;
}
int main()
{
TheOnlyInstance *pt=TheOnlyInstance::GetTheOnlyInstance();
pt->OtherMethod();
return 0;
}
賦值操作符重載。這點很容易犯錯的,稍不注意,就讓人覺得你沒有經驗。請看如下代碼:
class BaseDMA
{
private:
char *label;
public:
BaseDMA(){};
~BaseDMA(){};
BaseDMA & operator=(const BaseDMA &rs);
};
BaseDMA & BaseDMA::operator =(const BaseDMA &rs)
{
if(this==&rs)
return *this;
delete [] label;
label=new char[strlen(rs.label)+1];
strcpy(label,rs.label);
return *this;
}
我們很容易忽略的一點就是a=a的情況。
請看如下代碼
#include <stdio.h>
int main()
{
int i;
int a[3]={1,2,3};
int *b[3];
for(i=0;i<3;i++)
{
b[i]=&a[i];
printf("%d/n",b[i]);
}
printf("%d",b[1]-b[0]);
return 0;
}
它的輸出如下所示(在我的機器上):
1245040
1245044
1245048
1
我就一不小心中了陷阱,將最後一個寫成了4,暈了。
隨後的東西留到以後繼續慢慢想(to be continued)