說明:
下面的題目,有的是看書的時候,弄懂一個問題後,突然想着要是作爲面試題,面試官會怎麼問?有的來源於網絡。權當總結,備戰面試。
題目1:
class A
{
private:
int i;
int j;
};
class B:public A
{
private:
int k;
}
請編寫類A和類B的構造函數,複製構造函數,複製構造函數?
我相信大部分同學看到問題,竊喜。這個嘛簡單嘛。提筆就來。
#include<iostream>
using namespace std;
class A
{
public:
A()
{
i=0;
j=0;
}
A(int i1,int j1)
{
i=i1;
j=j1;
}
A(const A &a)
{
i=a.i;
j=a.j;
}
A& operator =(const A &a)
{
i=a.i;
j=a.j;
return *this;
}
virtual void print()
{
cout<<"i="<<i<<" j="<<j;
}
private:
int i;
int j;
};
class B:public A
{
public:
B(int i,int j,int k1):A(i,j)
{
k=k1;
}
B(const B& b)
{
k=b.k;
}
B& operator =(const B &b)
{
k=b.k;
return *this;
}
virtual void print()
{
A::print();
cout<<" k="<<k;
}
private:
int k;
};
寫個main函數測試一下:
int main()
{
B pc(1,5,6);
pc.print();
cout<<endl;
B pc2(2,2,3);
pc2.print();
cout<<endl;
pc2=pc;
pc2.print();
cout<<endl;
}
運行結果一看(結果有誤):
i=1 j=5 k=6
i=2 j=2 k=3
i=2 j=2 k=6
當把自定義的複製構造函數,和賦值構造函數註釋掉後,結果是正確的。
i=1 j=5 k=6
i=2 j=2 k=3
i=1 j=5 k=6
爲什麼會這樣?
在自定義複製構造函數和賦值構造函數裏面,並沒有對繼承的成員進行賦值,想當然的認爲,會自動調用父類的複製構造函數或賦值構造函數。其實這就是effective c++條例12,複製對象時勿忘其每一部分。
正確的做法
在子類的自定義賦值或者複製函數裏面,調用基類的賦值或者複製函數,實現基類成員的賦值。
B(const B& b):A(b)
{
k=b.k;
}
B& operator =(const B &b)
{
A::operator=(b);
k=b.k;
return *this;
}
題目2:
const char *p 與char * const q的區別?
答:
const位於星號的左邊,則const修飾指針所指向的變量,即指針指向的是常量。
const位於星號的右邊,則const修飾指針,即指針本身是個常量。
題目3:
空類大小爲多少?
答:1
題目4:
什麼時候需要自定義複製構造函數等?
答: 編譯器生成的複製構造函數無法滿足要求時,需要自定義複製構造函數。最簡單的,但成員變量裏面有指針。
題目5:
智能指針的實現?
題目6:
string *str,int *pi,vector<int> *ivec,指針大小是否相同?
答:指針大小相同,指針尋址出來的object類型不同,是因爲指針類型教導編譯器如何解釋某個特定地址的內存內容和大小。
題目7:
c++基類構造順序是以基類聲明順序爲順序。
題目8:
問:用戶自定義類A,並且自定一個函數 A test()函數,請問函數寫法1,和函數寫法2,有沒有區別?
寫法1:
A test()
{
return A();
}
寫法2:
A test()
{
A a;
return a;
}
答案:有區別的。
寫法1的:編譯器直接把臨時變量創建並初始化在外部存儲單元,省去了拷貝與析構步驟。
寫法2:首先,temp 對象被創建,同時完成初始化;然後拷貝構造函數把temp 拷貝到保存返回值的外部存儲單元中;最後,temp 在函數結束時被銷燬(調用析構函數)
爲了檢驗這句話,先實現類A,並標記構造函數,複製構造函數等。
#include <iostream>
using namespace std;
class A
{
public:
A(int i=0):num(i)
{
cout << "構造函數 " << num << endl;
}
A(const A &a)
{
num = a.num;
cout << "複製構造函數 " << num << endl;
}
A& operator=(const A &a)
{
cout << "賦值構造函數 " << num << "賦值爲" << a.num <<endl;
num = a.num;
return *this;
}
~A()
{
cout << "析構函數 " << num << endl;
}
private:
int num;
};
A test(int i)
{
A a(i);
return a;
}
A test2(int i)
{
return A(i);// 這個時候,編譯器直接把臨時變量初始化,並創建在外部存儲單元
}
下面是測試:
標號 | 調用代碼 | 輸出結果 |
1 | A a = test(1); | 構造函數 1 複製構造函數 1 析構函數 1 析構函數 1 |
2 | A a = test2(1); | 構造函數 1 析構函數 1 |
3 | A a(3); a = test(1); |
構造函數 3 構造函數 1 複製構造函數 1 析構函數 1 賦值構造函數 3賦值爲1 析構函數 1 析構函數 1 |
4 | A a(3); a = test2(1); |
構造函數 3 構造函數 1 賦值構造函數 3賦值爲1 析構函數 1 析構函數 1 |
題目9:
下面代碼執行結果:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void f()
{
cout<<"Base::f()"<<endl;
}
void d()
{
cout<<"Base::d()"<<endl;
}
};
class Derived:public Base
{
public:
void f()//覆蓋基類f()
{
cout<<"Derived::f()"<<endl;
}
void d()//隱藏基類的d()。注意,覆蓋!=隱藏
{
cout<<"Derived::d()"<<endl;
}
};
int main()
{
Derived d;
Base *pb=&d;
Derived *pd=&d;
pb->f();
pd->f();
pb->d();
pd->d();
}
答案:
Derived::f()
Derived::f()
Base::d()
Derived::d()
(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
(2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。