1、原題如下:
#include <iostream>
using namespace std;
class Test
{
public:
static int getA();
int getB();
virtual int getC();
int a;
static int c;
};
int Test::getA()
{
return 0;
}
int Test::getB()
{
return 0;
}
int Test::getC()
{
return 0;
}
int main()
{
Test *ptr = (Test*)malloc(sizeof(Test));
//下面的幾個調用,哪些會出現問題?
ptr->getA();
ptr->getB();
ptr->getC();
ptr->a;
ptr->c;
return 0;
}
答案:所有語句都能通過編譯,但是運作時,ptr->getC();這條語句會出錯,crash。
分析:考察的地方有malloc和虛函數。
虛函數調用會crash,其他正常。因為其他在編譯期間可以确定位址。
malloc調用,不會像new一樣調用構造函數(free不會調用析構函數),所有虛函數指針都不會配置設定。
參考文獻:《深度探索C++對象模型》
2、原題如下:
以下代碼是否完全正确,執行可能得到的結果是____。
A.程式正常運作
B.程式編譯錯誤
C.程式崩潰
D.程式死循環
#include <iostream>
using namespace std;
class A
{
int i;
};
class B
{
A *p;
public:
B(){p = new A;}
~B(){delete p;}
};
void sayHello(B b)
{
}
int main()
{
B b;
sayHello(b);
}
答案:C.首先程式中沒有編譯錯誤,也沒有死循環(程式中沒有循環哪裡來的死循環)。有些初學者可能會說main函數沒有寫return 0,main函數不顯性的寫return,編譯器也會幫你做的。當類中存在指針類型的成員變量時指派和析構要格外注意,這道題的問題就出在類B對象b中的指針p被析構了兩次。
分析:當執行完成B b這句話後,在b中就構造了一個類A的指針對象p,當調用sayHello(b)函數時系統将會調用類B的指派構造函數構造一個類B的執行個體bStep(為了友善下面的叙述随便起了一個名字)傳入到sayHello函數中(問題就出在bStep這個執行個體中),這裡當sayHello執行完成後,之前構造的執行個體bStep将被析構(執行delete p)。然後程式繼續開心的執行,直到執行完main函數後系統将會析構b,當b被析構時将再次執行delete p。這樣p就被析構了兩遍導緻程式崩潰。
我們把代碼增加一些輸出資訊後大家就更容易看了:
#include <iostream>
using namespace std;
class A
{
int i;
};
class B
{
A *p;
public:
B(){ printf("構造\n"); p = new A; }
~B(){ printf("析構\n"); delete p; }
B(const B &b){ printf("拷貝構造\n"); }
};
void sayHello(B b)
{
}
int main()
{
B b;
sayHello(b);
}
控制台輸出如下(2次構造函數,2次析構函數):
構造
拷貝構造
析構
正确的方法應該把sayHello函數寫成這樣:
void sayHello(const B& b)
{
}
控制台輸出如下(1次構造函數,1次析構函數):
使用引用的方法效率高,沒有拷貝構造函數被調用,因為沒有新對象被建立。const目的是避免對象被改變。
參考文獻:《Effective C++ 第三版》條款20:甯以pass-by-reference-to-const替換pass-by-value