C++primer 相關學習3

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構造方法");  
    }  
}

 

 
 

輸出結果:

  1. 初始化塊主要用於對象的初始化操作,在創建對象時調用,可以用於完成初始化屬性值、加載其他的類的功能。
  2. 初始化塊和構造方法功能類似,可以再創建對象的時候完成一些初始化的操作,一般的情況下,構造方法初始化和初始化塊初始化可以通用。
  3. 構造方法在初始化的時候可以通過參數傳遞,但是初始化塊不能,初始化塊的初始化在構造方法之前執行,如果搞糟方法多次重載,此時可以考慮構造方法中共通的代碼放到初始化塊中進行初始化。

補充:

靜態初始化塊和非靜態初始化塊的區別?

  1. 非靜態初始化塊主要是用於對象的初始化操作,在每次創建對象的時都要調用一次,其執行順序在構造方法之前。
  2. 在初始化塊之前有static修飾,則爲靜態初始化塊。由於非靜態成員不能再靜態方法中使用,同樣也不能在靜態初始化塊中,因此,靜態初始化塊主要用於初始化靜態變量和靜態方法,靜態初始化塊只調用一次,是在類的第一次加載到內存時,並非一定要創建對象才執行。
  3. 靜態初始化塊比非靜態初始化塊先執行

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章