1. 為什麼Delete會出錯?
class CBase
{
public:
CBase() { cout <<"CBase" << endl; }
virtual ~CBase() { cout <<"~CBase" << endl;}
};
classCDerived : public CBase
{
public:
CDerived() { cout <<"CDerived" << endl; }
~CDerived() { cout <<"~CDerived" << endl; }
};
int main()
{
CBase base;
CBase* pBase = new CBase;
pBase = &base;
delete pBase; //運作時報錯!
}
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iNwQDZxcjN4UGOmFDZ2QWYzQDM0EWZwkzN5ITZhdjZ28CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
【分析如下】:
1.pBase指向了棧區記憶體,那是系統管理的空間,不能用delete釋放的。
2.程式在堆區new的空間最後沒有被釋放,造成了記憶體洩露。
3.最好不要随便把申請到堆區空間的指針指向别處,至少也要有一個指針指向申請的空間。以便最後釋放的是自己申請的那塊記憶體。
【修正後做法】:
int main()
{
CBase base;
CBase* pBase = new CBase;
CBase* pBase2 = pBase; //至少也要有一個指針指向申請的空間
pBase = &base;
delete pBase2; //以便最後釋放的是自己申請的那塊記憶體。
} //運作時不再報錯!
【再深入點】:程式有兩個問題:
1.記憶體洩露,new出來的沒delete;
2.兩次析構;base不是new出來,在生命周期結束(也就是你函數結束的時候)會自動釋放,你主動調用delete将其析構,系統在函數結束時又會對其析構,是以才會報錯。而且報錯的地方應該是程式退出時。
2. 類中靜态常成員變量的定義?
#include<iostream>
usingnamespace std;
//可以在類的聲明中對常量的類變量進行指派
//VS2008可以,vc6.0不可以。和編譯器有關。
class myclass
{
public:
static const int i=20; //隻有類的靜态常量資料成員才可以在類中初始化。
};
const int myclass::i = 10;
int main()
{
cout<<myclass::i<<endl;
return 0;
}
3.重載和多态的關系?
不同點 | 重載overload | 覆寫override |
---|---|---|
1.是否支援多态? | 不支援 | 支援 |
2.存在形式? | 可以在類中或在C++語言中都可以展現 | 存在于類中父類、子類之間。 |
3.參數清單、傳回值 | 參數清單或傳回值不同,或二者都不同。 | 參數清單、傳回名額必須相同。 |
4.輸出格式:printf用!
%a(%A) | 浮點數、十六進制數字和p-(P-)記數法(C99) |
---|---|
%c | 字元 |
%d | 有符号十進制整數 |
%f | 浮點數(包括float和doulbe) |
%e(%E) | 浮點數指數輸出[e-(E-)記數法] |
%g(%G) | 浮點數不顯無意義的零"0" |
%i | 有符号十進制整數(與%d相同) |
%u | 無符号十進制整數 |
%o | 八進制整數 e.g. 0123 |
%x(%X) | 十六進制整數0f(0F) e.g. 0x1234 |
%p | 指針 |
%s | 字元串 |
5.一個參數或指針可以既是const又是volatile)解讀?
Volatile 以防止編譯器将其優化成從寄存器中讀取。一個定義為volatile的變量時說這個變量可能會被意想不到地改變,這樣編譯器就不會去假設這個變量的值。精确地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用儲存在寄存器裡的備份。
一個參數可以既是const又是volatile,const是不讓程式修改,volatile是意想不到的改變,不是程式修改。一個指針也可以是volatile,中斷服務子程式修改一個指向buffer的指針。
6.
Little Endian低位元組序(由低位元組—>高位元組存儲);計算機預設的為低位元組序。
High Endian高位元組序(由高位元組—>低位元組存儲);
typedef struct bitstruct
{
int b1:5;
int b2:2;
int b3:2;
}bitstruct;
int main()
{
bitstruct b;
memcpy(&b,"EMCEXAMINATION",sizeof(b));
cout << sizeof(b) << endl;
printf("%d,%d\n",b.b1,b.b2);//5,-2
return 0;
}
[解讀]:
1.sizeof(b)=4;即4個位元組的大小。
2.memcpy将”EMC …”存入b中。
3.實質b中隻有5+2+2,9位。即對應字元也隻有”EM”.E的ASCII碼為0X45,M的ASCII碼為0X4D。
滿足高位元組—>低位元組存儲0X4D0X45,對應二進制位:01001101 0100 0101。
4.對應的b1,滿足(低位元組存放于低位)取得後低位址的5位0 0101,首位為0代表正數,大小為5。
對應的b2,取10,首位為1代表負數,取補碼後得到b2=-2。
27.輸出結果?
int main()
{
int a[5][2] = {0,1,2,3,4,5,6,7,8,9};
int *p = a[0];
int (*p2)[2] = &a[1];
++p;
++p2;
printf("%d\n",*p); //1 p 是整型指針,初始指向元素0,加1指向1
printf("%d\n",**p2); //4 p2是含2個元素的數組指針,初始指向元素2,該指針加1是向後移動2個資料,是以指向4
printf("%d \n",p2[1][2]); //如何解讀? 見下解讀。
return 0;
}
1 | ||
---|---|---|
0 (p) | 1(++p後) | |
2 (P2指向) | 3 | |
2 | 4 (++p2後) | 5 |
6 (p2+1後) | 7 | |
4 | 8 (p21) | 9 |
解讀:
p2是一個指針,是一個指向包含兩個元素數組的指針變量。和普通的指針不同的地方時它指向的長度為2。(*p2)[2]和a是等價的。
對于p21,此時p2指向4,前一個下标1就是p2指針再加1指向6,後一個下标加2移動2個元素,指向了8。
28.拷貝構造輸出結果?
class A
{
static int objectCount;
public:
A()
{
objectCount++;
cout << "A():" <<objectCount << endl;
}
A(const A& r)
{
objectCount++;
cout << "A(const A&r):" << objectCount << endl;
}
~A()
{
objectCount--;
cout << "~A():"<< objectCount << endl;
}
};
intA::objectCount = 0;
A f(A x)
{
cout << endl << "Begin: f(A x)" << endl;
return x; //【臨時對象】調用預設拷貝構造函數A(const A& r):3
} //~A():2
int main()
{
A h; //A():1
A h2 = f(h); //調用預設拷貝構造函數A(constA& r):2
cout << endl <<"End(main): f(A x)" << endl << endl;
return 0;
} //~A():1 析構h2
//~A():0 構函h
29.四類強制類型轉換
類型 | 示意 | 舉例 |
---|---|---|
static_cast | 1.類型轉換,編譯器隐式執行的任何類型都可由static_cast顯示完成;2.使用類型資訊執行轉換,在轉換執行必要的檢測(越界檢測、類型檢查),操作相對安全; | int—>double int ival;double result = static_cast ival |
const_cast | 轉換掉對象的const屬性 | 下舉例 |
dynamic_cast | 運作時類型檢查,用于繼承體制下的由上到下的轉換downcast。 | |
reinterpret_cast | 1.僅僅重新編譯了給定對象的比特模型,而沒有進行二進制轉換;2.為操作數提供低層次的重新解釋。 |
舉例:
//const_cast 執行個體.
class B
{
public:
int m_num;
};
int main()
{
B b0;
b0.m_num = 100;
const B b1 = b0;
cout << b0.m_num<< " " << b1.m_num << endl;
// 以下修改const對象的值是錯誤的。
// b1.m_num = 355;
// cout << b1.m_num <<endl; // error C2166: l-value specifiesconst object
//以下使用const_cast是正解.
const_cast<B&>(b1).m_num =355;
cout<< b1.m_num << endl;
return 0;
}
//reinterpret_cast執行個體
int main()
{
int n = 9;
double dval =reinterpret_cast<double& >(n);
double dval_new =static_cast<double>(n); //成功.
//[僅僅複制了n的比特位到d,沒有進行必要的分析]
cout << dval << endl;//2.64214e-308
cout << dval_new << endl;//9
return 0;
}
//dynamic_cast執行個體
class B
{
public:
B() { cout << "B()"<< endl; }
~B() { cout << "~B()"<< endl; }
};
class C :public B
{
public:
C() { cout << "C()"<< endl; }
~C() { cout << "~C()"<< endl; }
};
class D :public C
{
public:
D(){ cout << "D()"<< endl; }
~D(){ cout << "~D()"<< endl; }
};
void f(D* pd)
{
C* pc =dynamic_cast<C*>(pd); // ok: C isa direct base class
// pc points to C subobject of pd
B* pb =dynamic_cast<B*>(pd); // ok: B isan indirect base class
// pb points to B subobject of pd
}
int main()
{
D objd;
f(&objd);
return 0;
}
30.持續更新中......
30.如何在C/C++中顯示目前程式所在的檔案名及行号。
——這個當時沒答上來,見過沒記住。今天查了下MSDN如下:
__FILE__, //用于顯示檔案名的宏 %s, 格式如【F:\NeuSoftDemo\NeuSoftDemo.cpp】;__LINE__, //用于顯示行号的宏 %d,格式如【12】;
擴充》》__DATE__, //用于顯示目前日期,格式如【Sep 18 2012】 %s; __TIME__, //用于顯示目前時間,格式如【09:45:01】 %s;
__TIMESTAMP__,//用于顯示目前日期和時間,格式如【Tue Sep 18 09:48:07 2012】 %s。
31.程式糾錯題:
[cpp] view plaincopy
int main(int argc,char* argv[])
{
char str[5][] = {"First","Second","Thrid","Four","Five"};
char* p[] = {str[4],str[3],str[2],str[1],str[0]};
for(int i = 0; i < 5; i++)
{
printf("%c\n",*(p+i));
}
return 0;
}
int main(int argc, char* argv[])
{
char str[5][] = {"First","Second","Thrid","Four","Five"};
char* p[] = {str[4],str[3],str[2],str[1],str[0]};
for(int i = 0; i < 5; i++)
{
printf("%c\n",*(p+i));
}
return 0;
}
個人感覺如下:
錯誤1: char str[5][] 定義出錯,需要指定第一維的個數,改為char str[][5]嗎?但後面的字元串如"Second"6個字元,還有'0'。改為:char* str[5]比較穩妥;
錯誤2: printf("%cn",(p+i)); 顯然(p+i) 等價于p[i]存儲的是字元串,是以%c應該改為%s。(%c列印的是單個字元,%s列印的是字元串)。
修正後如下:
[cpp]
int main(int argc,char* argv[])
{
char *str[5] = {"First","Second","Thrid","Four","Five"};
char* p[] = {str[4],str[3],str[2],str[1],str[0]};
for(int i = 0; i < 5; i++)
{
printf("%s\n",*(p+i));
}
return 0;
}
33.遞歸與非遞歸實作二分查找。
//非遞歸實作-
[cpp] view plaincopy
void binarySearchUncycle(int nArr[],int nSize,int nSearchVal)
{
int nLow = 0;
int nHigh = nSize-1;
int nMid = 0;
bool bFound = false;
while(nLow <= nHigh)
{
nMid = (nLow + nHigh)/2;
cout << "nMid =" << nMid << endl;
if(nArr[nMid] == nSearchVal)
{
bFound = true;
break;
}
else if(nArr[nMid] > nSearchVal)
{
nHigh = nMid-1;
}
else
{
nLow = nMid+1;
}
}//end while
if(bFound)
{
cout << "The Elem " << nSearchVal << " is Exist in the array!" << endl;
}
else
{
cout << "The Elem " << nSearchVal << " is Not Exist in the array!" << endl;
}
}
[cpp]
//遞歸實作二分查找
void binarySearchCycle(int nArr[],int low, int high, int nSearchVal)
{
int nLow = low;
int nHigh = high;
int nMid = 0;
bool bFound = false;
nMid = (nLow + nHigh)/2;
if(nArr[nMid] == nSearchVal)
{
bFound = true;
cout << "The Elem " << nSearchVal << " is Exist in the array!" << endl;
return;
}
else if(nArr[nMid] > nSearchVal)
{
nHigh = nMid-1;
binarySearchCycle(nArr,nLow,nHigh,nSearchVal);
}
else
{
nLow = nMid+1;
binarySearchCycle(nArr,nLow,nHigh,nSearchVal);
}
}