1,對象的構造通過構造函數來完成,和類名相同且沒有返回值,這個時候只有參 數一個特性,構造函數可以自定義參數,這個參數一般而言就是對類進行初始 化來使用的;帶有參數的構造函數的意義在於可以使得每個對象有不同的初始 化狀態(生活中每個事物必然包含自己的初始化狀態,不如人的出生,面向對 象用來將生活中的事物映射的程序設計領域,所以現實世界的情況都必須可以 用面向對象的語言來描述,因此帶有參數的構造函數就是非常必要的);
2,帶有參數的構造函數:
1,構造函數可以根據需要定義參數;
2,一個類中可以存在多個重載的構造函數;
3,構造函數的重載遵循 C++ 重載的規則;
4,代碼示例:
1 class Test 2 { 3 public: 4 Test(int v) 5 { 6 // use v to initialize member 7 } 8 };
3,對象定義和對象聲明的區別:
1,對象定義:申請對象的空間並調用構造函數;
1,第一步,必須申請對象所占用的內存空間;
2,第二步,調用構造函數;
2,對象聲明:告訴編譯器存在這樣一個對象;
1,對象在哪里定義的不知道,鏈接的時候回去找;
2,預處理,編譯器對源代碼進行檢查並生成目標文件,鏈接器在各個目標文件中尋找目標文件存在的一些名字;
3,對象聲明時,沒有對象定義時的兩個步驟;
3,代碼示例:
1 Test t; // 定義對象並調用構造函數; 2 3 Int main() 4 { 5 // 告訴編譯器存在名為 t 的Test對象; 6 extern Test t; 7 8 return 0; 9 }
4,對象的聲明中可以在構造函數參數中給出默認值,對象的定義中不能夠在構 造函數參數中給出默認值;
4,構造函數的自動調用(第一種初始化對象方式):
1 #include <stdio.h> 2 3 class Test 4 { 5 public: 6 Test() 7 { 8 printf("Test()\n"); 9 } 10 11 Test(int v) 12 { 13 printf("Test(int v), v = %d\n", v); 14 } 15 }; 16 17 int main() 18 { 19 Test t; // 調用 Test() 20 Test t1(1); // 初始胡第一種方式的參數式自動調用,調用 Test(int v);這里也是定義對象,看上去非常像函數調用,但是這里是告訴編譯器要調用帶有參數的函數,由重載規則確定調用的是那個構造函數; 21 Test t2 = 2; // 初始化第一種方式的賦值式自動調用,調用 Test(int v);C 語言中初始化的方法,定義一個變量后,立即指明一個值,通過賦值符號指明;這在面向對象中其實也是對象的定義,並且指明想用右值初始化左值; 22 23 t = t2; // 這是賦值操作,這里運行后不會調用構造函數,沒有打印語句;初始化會調用構造函數,賦值則看后續課程; 24 25 int i = 1; // 用 1 對 i 進行初始化; 26 i = 1; // 用 1 對 i 進行賦值;賦值和初始化是不同的;在面向對象當中,不同在於初始化是要調用構造函數的; 27 28 int i(100); // 初始化的第二種寫法,同 int i = 100;; 29 30 printf("i = %d\n", i); 31 32 return 0; 33 }
1,實驗結果說明:
1,初始化和賦值看上去相同之處在於當初始化用賦值符號表達的時候;
2,不同之處在於初始化要調用構造函數,而賦值不會;
5,構造函數的調用:
1,一般情況下,構造函數在對象定義時被自動調用;
2,一些特殊情況下,需要手工調用構造函數:
1,如何創建對象數組;
6,構造函數的手工調用(第二種初始化對象方式)編程實驗:
1 #include <stdio.h> 2 3 class Test 4 { 5 private: 6 int m_value; 7 public: 8 Test() 9 { 10 printf("Test()\n"); 11 12 m_value = 0; 13 } 14 Test(int v) 15 { 16 printf("Test(int v), v = %d\n", v); 17 18 m_value = v; 19 } 20 int getValue() 21 { 22 return m_value; 23 } 24 }; 25 26 int main() 27 { 28 Test ta[3]; // 按照 C 語言的方法定義 3 個 Test 對象的數組 ta;結果調用了 3 個 Test() 函數; 29 30 for(int i=0; i<3; i++) // 循環結果打印出 3 個 0,這不一定是我們想要的;編譯器默認的調用了 Test(); 31 { 32 printf("ta[%d].getValue() = %d\n", i , ta[i].getValue()); 33 } 34 35 Test ta[3] = {Test(), Test(1), Test(2)}; // 手動調用構造函數; 36 37 for(int i=0; i<3; i++) // 循環結果為 0 1 2;分別調用了相應的構造函數; 38 { 39 printf("ta[%d].getValue() = %d\n", i , ta[i].getValue()); 40 } 41 42 Test t = Test(100); // 初始化第二種方式,手工調用構造函數; 43 44 printf("t.getValue() = %d\n", t.getValue()); 45 46 return 0; 47 }
7,小實例:
1,需求:開發一個數組類解決原生數組的安全性問題:
1,提供函數獲取數組長度;
1,C++ 要兼容 C 語言中的數組,但是 C 語言中的數組沒有長度信息,用着用着就越界了;
2,提供函數獲取數組元素;
3,提供函數設置數組元素;
8,數組類的實現編程實驗:
1,IntArray.h 文件:
1 #ifndef _INTARRAY_H_ 2 #define _INTARRAY_H_ 3 4 class IntArray 5 { 6 private: 7 int m_length; 8 int* m_pointer; 9 public: 10 IntArray(int len); 11 int length(); 12 bool get(int index, int& value); // 用 bool 類型作為返回值是為了安全性,安全性的體現見數組類的實現; 13 bool set(int index ,int value); 14 void free(); // 用來釋放 m_pointer 指向的堆空間 15 }; 16 17 #endif
2,IntArray.cpp 文件:
1 #include "IntArray.h" 2 3 IntArray::IntArray(int len) // 不加作用域分辨符時,是全局函數(此時僅名字前綴相同而已),所以要加作用域分辨符指明是數組類中的函數; 4 { 5 m_pointer = new int[len]; // new int(len) 指的是設置初始值; 6 7 for(int i=0; i<len; i++) 8 { 9 m_pointer[i] = 0; 10 } 11 12 m_length = len; 13 } 14 15 int IntArray::length() 16 { 17 return m_length; 18 } 19 20 bool IntArray::get(int index, int& value) 21 { 22 bool ret = (0 <= index) && (index < length()); // 進行安全性檢查,是安全性的體現; 23 24 if( ret ) 25 { 26 value = m_pointer[index]; // 通過引用返回一個值; 27 } 28 29 return ret; // 越界則返回 false; 30 } 31 32 bool IntArray::set(int index, int value) 33 { 34 bool ret = (0 <= index) && (index < length()); 35 36 if( ret ) 37 { 38 m_pointer[index] = value; 39 } 40 41 return ret; 42 } 43 44 void IntArray::free() 45 { 46 delete[]m_pointer; 47 }
3,IntArray 的使用:
1 #include <stdio.h> 2 #include "IntArray.h" 3 4 int main() 5 { 6 IntArray a(5); 7 8 for(int i=0; i<a.length(); i++) 9 { 10 a.set(i, i + 1); 11 } 12 13 for(int i=0; i<a.length(); i++) 14 { 15 int value = 0; 16 17 if( a.get(i, value) ) 18 { 19 printf("a[%d] = %d\n", i, value); 20 } 21 } 22 23 a.free(); 24 25 return 0; 26 }
4,這里展示了面向對象的強大,我們可以通過類來封裝一些之前學習到的概念,並且可以將這些概念上的缺陷通過封裝來彌補開來;
9,小結:
1,構造函數可以根據需要定義參數;
2,構造函數之間可以存在重載關系;
3,構造函數遵循 C++ 中重載函數的規則;
4,對象定義時會觸發構造函數的調用;
1,構造函數調用方式分為自動調用和手工調用兩種;
2,自動調用的形式又分為參數式和賦值式;
3,即初始化時,分為兩種調用方式三種書寫形式;
5,在一些情況下可以手動調用構造函數;
