1. 程式人生 > 其它 >C++建構函式詳解

C++建構函式詳解

C++中,有一種特殊的成員函式,它的名字和類名相同,沒有返回值,不需要使用者顯式呼叫(使用者也不能呼叫),而是在建立物件時自動執行。這種特殊的成員函式就是建構函式(Constructor)。

我們通過成員函式 setname()、setage()、setscore() 分別為成員變數 name、age、score 賦值,這樣做雖然有效,但顯得有點麻煩。有了建構函式,我們就可以簡化這項工作,在建立物件的同時為成員變數賦值,請看下面的程式碼(示例1):

  1. #include <iostream>
  2. using namespace std;
  3. class Student{
  4. private:
  5. char *m_name;
  6. int m_age;
  7. float m_score;
  8. public:
  9. //宣告建構函式
  10. Student(char *name, int age, float score);
  11. //宣告普通成員函式
  12. void show();
  13. };
  14. //定義建構函式
  15. Student::Student(char *name, int age, float score){
  16. m_name = name;
  17. m_age = age;
  18. m_score = score;
  19. }
  20. //定義普通成員函式
  21. void Student::show(){
  22. cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl;
  23. }
  24. int main(){
  25. //建立物件時向建構函式傳參
  26. Student stu("小明", 15, 92.5f);
  27. stu.show();
  28. //建立物件時向建構函式傳參
  29. Student *pstu = new Student("李華", 16, 96);
  30. pstu -> show();
  31. return 0;
  32. }

執行結果:
小明的年齡是15,成績是92.5
李華的年齡是16,成績是96

該例在 Student 類中定義了一個建構函式Student(char *, int, float)

,它的作用是給三個 private 屬性的成員變數賦值。要想呼叫該建構函式,就得在建立物件的同時傳遞實參,並且實參由( )包圍,和普通的函式呼叫非常類似。

在棧上建立物件時,實參位於物件名後面,例如Student stu("小明", 15, 92.5f);在堆上建立物件時,實參位於類名後面,例如new Student("李華", 16, 96)

建構函式必須是 public 屬性的,否則建立物件時無法呼叫。當然,設定為 private、protected 屬性也不會報錯,但是沒有意義。

建構函式沒有返回值,因為沒有變數來接收返回值,即使有也毫無用處,這意味著:

  • 不管是宣告還是定義,函式名前面都不能出現返回值型別,即使是 void 也不允許;
  • 函式體中不能有 return 語句。

建構函式的過載

和普通成員函式一樣,建構函式是允許過載的。一個類可以有多個過載的建構函式,建立物件時根據傳遞的實參來判斷呼叫哪一個建構函式。

建構函式的呼叫是強制性的,一旦在類中定義了建構函式,那麼建立物件時就一定要呼叫,不呼叫是錯誤的。如果有多個過載的建構函式,那麼建立物件時提供的實參必須和其中的一個建構函式匹配;反過來說,建立物件時只有一個建構函式會被呼叫。

對示例1中的程式碼,如果寫作Student stu或者new Student就是錯誤的,因為類中包含了建構函式,而建立物件時卻沒有呼叫。

更改示例1的程式碼,再新增一個建構函式(示例2):

  1. #include <iostream>
  2. using namespace std;
  3. class Student{
  4. private:
  5. char *m_name;
  6. int m_age;
  7. float m_score;
  8. public:
  9. Student();
  10. Student(char *name, int age, float score);
  11. void setname(char *name);
  12. void setage(int age);
  13. void setscore(float score);
  14. void show();
  15. };
  16. Student::Student(){
  17. m_name = NULL;
  18. m_age = 0;
  19. m_score = 0.0;
  20. }
  21. Student::Student(char *name, int age, float score){
  22. m_name = name;
  23. m_age = age;
  24. m_score = score;
  25. }
  26. void Student::setname(char *name){
  27. m_name = name;
  28. }
  29. void Student::setage(int age){
  30. m_age = age;
  31. }
  32. void Student::setscore(float score){
  33. m_score = score;
  34. }
  35. void Student::show(){
  36. if(m_name == NULL || m_age <= 0){
  37. cout<<"成員變數還未初始化"<<endl;
  38. }else{
  39. cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<endl;
  40. }
  41. }
  42. int main(){
  43. //呼叫建構函式 Student(char *, int, float)
  44. Student stu("小明", 15, 92.5f);
  45. stu.show();
  46. //呼叫建構函式 Student()
  47. Student *pstu = new Student();
  48. pstu -> show();
  49. pstu -> setname("李華");
  50. pstu -> setage(16);
  51. pstu -> setscore(96);
  52. pstu -> show();
  53. return 0;
  54. }

執行結果:
小明的年齡是15,成績是92.5
成員變數還未初始化
李華的年齡是16,成績是96

建構函式Student(char *, int, float)為各個成員變數賦值,建構函式Student()將各個成員變數的值設定為空,它們是過載關係。根據Student()建立物件時不會賦予成員變數有效值,所以還要呼叫成員函式 setname()、setage()、setscore() 來給它們重新賦值。

建構函式在實際開發中會大量使用,它往往用來做一些初始化工作,例如對成員變數賦值、預先開啟檔案等。

預設建構函式

如果使用者自己沒有定義建構函式,那麼編譯器會自動生成一個預設的建構函式,只是這個建構函式的函式體是空的,也沒有形參,也不執行任何操作。比如上面的 Student 類,預設生成的建構函式如下:

Student(){}

一個類必須有建構函式,要麼使用者自己定義,要麼編譯器自動生成。一旦使用者自己定義了建構函式,不管有幾個,也不管形參如何,編譯器都不再自動生成。在示例1中,Student 類已經有了一個建構函式Student(char *, int, float),也就是我們自己定義的,編譯器不會再額外新增建構函式Student(),在示例2中我們才手動添加了該建構函式。

實際上編譯器只有在必要的時候才會生成預設建構函式,而且它的函式體一般不為空。預設建構函式的目的是幫助編譯器做初始化工作,而不是幫助程式設計師。這是C++的內部實現機制,這裡不再深究,初學者可以按照上面說的“一定有一個空函式體的預設建構函式”來理解。

最後需要注意的一點是,呼叫沒有引數的建構函式也可以省略括號。對於示例2的程式碼,在棧上建立物件可以寫作Student stu()Student stu,在堆上建立物件可以寫作Student *pstu = new Student()Student *pstu = new Student,它們都會呼叫建構函式 Student()。

以前我們就是這樣做的,建立物件時都沒有寫括號,其實是呼叫了預設的建構函式。