1.因爲一旦某個形參被賦予了默認值,那麼它之後的形參都必須要有默認值。
(a) int ff(int a, int b = 0, int c = 0); //正確
(b) char *init(int ht = 24, int wd, char bckgrnd); //錯誤
2.constexpr函數的返回值類型及所有形參都得是字面值類型。
3.函數匹配
void f();
void f(int);
void f(int,int);
void f(double,double = 3.14);
(a) f(2.56, 42)
(b) f(42)
(c) f(42, 0)
(d) f(2.56, 3.14)
(a) void f(int, int); 和 void f(double, double = 3.14); 是可行函數。該調用具有二義性而不合法。
(b) void f(int); 是可行函數。調用合法。
(c) void f(int, int); 和 void f(double, double = 3.14); 是可行函數。void f(int, int); 是最佳匹配。
(d) void f(int, int); 和 void f(double, double = 3.14); 是可行函數。void f(double, double = 3.14); 是最佳匹配。
4.有無const修飾的函數的重載
(a)(b)都是合法的重載,(c)是非法的重載,頂層const不影響函數傳入的對象。
(a) int calc(int&, int&);
int calc(const int&, const int&);
(b) int calc(char*, char*);
int calc(const char*, const char*);
(c) int calc(char*, char*);
int calc(char* const, char* const);
5.函數指針
如果想要聲明一個函數指針,只需要用指針替換函數名即可。
bool (*pf)(const string &,const string &) 該函數的類型是bool
觀察pf前面有個*,所以pf是個指針,右側是形參列表,所
以證明指向的是一個函數,在觀察左邊,發現函數的返回值
是一個bool類型的。所以,pf是指向函數的指針,其中,該
函數的參數是兩個const類型的引用,返回值是bool類型。
bool *pf(const string &,const string &)// 表明pf是一個函數名,返回的是爲bool指針的函數。
6.返回函數指針的函數
int (*f1(int))(int *,int); 從內向外閱讀聲明,f1有形參列表,f1是函數,f1前面有*,所以f1返回的是
一個指針,進一步觀察,指針的類型本身也包含形參列表,因
爲指針指向函數,該函數的返回值是int.
7.
bool is_empty(string &s){
return s.empty();
}
一般對於不需要改變的代碼,將其設置爲const string &s,否則字符創和常量引用將無法賦值。
8.常量指針和常量引用不過是指針或者引用自以爲是罷了,他們覺得自己指向了常量,所以自覺地不改變對象的值,但是對象可以通過其他的方式進行更改。
9.extern:在每個用到的文件中需要用多個extern 聲明;
include:只需要在include各聲明一次,其它使用這些變量的只需要包含該頭文件即可.
大體上,你可以把extern 和 include 的區別當做是“零售”與“批發”的區別。include是批發,而extern 則是零售。
10.constexpr函數的返回值類型及所有形參都得是字面值類型。內聯函數的聲明和定義都放在頭文件中
11.
初始化是直接初始化數據成員,而賦值是先初始化再賦值。
class constref{
public:
constref(int ii);
private:
int i;
const int ci;
int &ri;
}
constref::constref(int ii)
{
i = ii;//正確
ci= ii;//錯誤,不能給ci賦值
ri =i;//錯誤 ,沒有初始化
}
//正確的方式應該是這樣
constref::constref(int ii):i(ii),ci(ii),ri(i){}
12.讓構造函數初始值的順序和成員聲明的順序保持一致
13.constexpr 函數必須包含一個返回語句。constexpr函數的參數和返回值必須是字面值類型。
14.如果在一個循環中插入或者刪除deque string vector 中的元素,不要緩存end返回的迭代器。
15.
程序使用動態內存有一下三種原因:
1.程序不知道自己需要使用多少對象。
2.程序不知道所需對象的精準類型
3.程序需要在多個對象間共享內存
16.
拷貝構造函數初始化發生在以下的地方
1.將一個對象作爲實參傳遞給一個非引用類型的形參
2.從一個返回類型爲非引用類型的函數返回一個對象
3.用花括號列表初始化一個數組中的元素或者一個聚合類中的成員。
17.三五法則
1。需要拷貝構造函數的類也需要拷貝和賦值操作
2。需要拷貝操作的類也需要賦值操作,反之亦然。
18.變量是左值,因此我們不能把一個右值引用直接綁定到一個變量上,即使這個變量是右值引用也不行。
19.什麼情況下一定要用初始化列表
- 常量成員,因爲常量只能初始化不能賦值,所以必須放在初始化列表裏面
- 引用類型,引用必須在定義的時候初始化,並且不能重新賦值,所以也要寫在初始化列表裏面
- 沒有默認構造函數的類類型,因爲使用初始化列表可以不必調用默認構造函數來初始化,而是直接調用拷貝構造函數初始化
20.
淺談對象的初始化順序
1.沒有繼承情況下的初始化順序
package InitializationOrder;
/**
* 沒有繼承的初始化順序
* @author TANJIAYI
*
*/
public class Test4 {
public static void main(String[] args) {
new Order();
}
}
class Order{
AAA s = new AAA("成員變量");
static AAA a = new AAA("靜態成員變量");
{
System.out.println("初始化塊");
}
static{
System.out.println("靜態初始化塊");
}
Order(){
System.out.println("構造方法");
}
}
class AAA{
public AAA(String str){
System.out.println(str);
}
}
輸出結果:
靜態成員變量
靜態初始化塊
成員變量
初始化塊
構造方法
結論:在沒有繼承的條件下,實例化一個對象,構造的先後順序是,靜態成員變量>靜態初始化塊>成員變量>初始化塊>構造方法
一般順序爲 先靜態,後非靜態,先變量,後初始化塊,再是構造方法
2.經典面試題
直接上代碼:
package InitializationOrder;
/**
* 初始化順序 靜態變量 靜態初始化塊 成員變量 初始化塊 構造函數
* @author TANJIAYI
*
*/
public class Test1 {
public static Test1 t1 = new Test1("t1");//第1步
public static int k = 0;
public static Test1 t2 = new Test1("t2");//第2步
public static int i = print("i");//第3步
public static int n = 99;//第4步
public int j = print("j");//第6步
{
print("構造");//第7步
}
static{
print("靜態");//第5步
}
public Test1(String str){
System.out.println((++k)+":"+str+" i="+i+" n="+n);
++i;
++n;
}
private static int print(String str) {
System.out.println((++k)+":"+str+" i="+i+" n="+n);//1:j i=0 n=0
++n;
return ++i;
}
public static void main(String[] args) {
Test1 test = new Test1("init");//第8步
}
}
輸出結果爲:
1:j i=0 n=0
2:構造 i=1 n=1
3:t1 i=2 n=2
1:j i=3 n=3
2:構造 i=4 n=4
3:t2 i=5 n=5
4:i i=6 n=6
5:靜態 i=7 n=99
6:j i=8 n=100
7:構造 i=9 n=101
8:init i=10 n=102
解題思路:
(1) 按照對象初始化順序依次執行,首先靜態變量從代碼中的第九行到第13行依次執行。
(2) 執行第1步,調用new Test1()方法,本方法是個構造方法,在執行前,類加載的時候先把k,i和n的值加載進來,初始值爲0,接着執行第14行成員變量j;調用print()方法,把j賦給str,所以27行代碼打印第一條輸出:1:j i=0 n=0,在執行初始化塊,15—17行,打印出2:構造 i=1 n=1,最後在執行Test1()構造方法,打印出 3:t1 i=2 n=2,第1步語句執行完畢,接着執行第2步。
(3) 分析同上,逐步打印出答案。
爲了好理解,下面附上一張圖,挨着挨着看思路還是很清楚的哦!圖畫得有點兒亂:
總結:大家只要掌握好執行的先後順序,仔細分析題,就沒有問題。
2.繼承情況下對象的初始化順序
屬性、方法、構造方法和自由塊都是類中的成員,在創建類的對象時,類中各成員的執行順序:
1.父類靜態成員和靜態初始化快,按在代碼中出現的順序依次執行。
2.子類靜態成員和靜態初始化塊,按在代碼中出現的順序依次執行。
3. 父類的實例成員和實例初始化塊,按在代碼中出現的順序依次執行。
4. 執行父類的構造方法。
5.子類實例成員和實例初始化塊,按在代碼中出現的順序依次執行。
6.執行子類的構造方法。
package InitializationOrder;
/**
* 繼承下的初始化順序
* @author TANJIAYI
*
*/
public class Test2 {
public static void main(String[] args) {
new Son();
}
}
class Parent{
{
System.out.println("parent中的初始化塊");
}
static{
System.out.println("parent中static初始化塊");
}
public Parent(){
System.out.println("parent構造方法");
}
}
class Son extends Parent{
{
System.out.println("son中的初始化塊");
}
static{
System.out.println("son中的static初始化塊");
}
public Son(){
System.out.println("son構造方法");
}
}
輸出結果:
- 初始化塊主要用於對象的初始化操作,在創建對象時調用,可以用於完成初始化屬性值、加載其他的類的功能。
- 初始化塊和構造方法功能類似,可以再創建對象的時候完成一些初始化的操作,一般的情況下,構造方法初始化和初始化塊初始化可以通用。
- 構造方法在初始化的時候可以通過參數傳遞,但是初始化塊不能,初始化塊的初始化在構造方法之前執行,如果搞糟方法多次重載,此時可以考慮構造方法中共通的代碼放到初始化塊中進行初始化。
補充:
靜態初始化塊和非靜態初始化塊的區別?
- 非靜態初始化塊主要是用於對象的初始化操作,在每次創建對象的時都要調用一次,其執行順序在構造方法之前。
- 在初始化塊之前有static修飾,則爲靜態初始化塊。由於非靜態成員不能再靜態方法中使用,同樣也不能在靜態初始化塊中,因此,靜態初始化塊主要用於初始化靜態變量和靜態方法,靜態初始化塊只調用一次,是在類的第一次加載到內存時,並非一定要創建對象才執行。
- 靜態初始化塊比非靜態初始化塊先執行