構造函數的作用是在建立對象時初始化對象,即為對象成員變量賦初始值,總與new運算符一起使用在建立對象的語句中。一個類可以有多個構造函數,可根據參數個數不同或類型不同來進行構造函數的重載。
- 預設構造函數
Student(); //沒有參數 //如果建立一個類沒有寫任何構造函數,系統會自動生成預設的無參構造函數 //系統自動生成的預設構造函數為空,什麼都不做初始化構造函數 //也可自己寫出預設構造函數,在函數體内進行變量的指派操作 Student() { num = ; age = ; }
- 初始化構造函數
可以看到,采用初始化構造函數時,對于類的成員變量的初始化有兩種方式,一種是使用初始化清單,一種是使用函數體内指派的方式。兩種方式的差別是:Student(int num, in age); //初始化構造函數有參數 //初始化構造函數的定義如下 //可以采用這種初始化清單,即:後進行變量初始化 Student(int n, int a):num(n), age(a){} //也可在函數體内進行初始化 Student(int n, int a) { num = n; age = a; }
- 内部資料類型(char, int……指針等)
class Animal { public: Animal(int weight, int height): //A初始化清單 m_weight(weight), m_height(height) { } Animal(int weight, int height) //B函數體内初始化 { m_weight = weight; m_height = height; } private: int m_weight; int m_height; };
對于這些内部類型來說,基本上是沒有差別的,效率上也不存在多大差異。
但是A和B方式不能同時存在。
-
無預設構造函數的繼承關系中
當使用了初始化構造函數時,如果不特别地寫出預設構造函數,系統是不會自動生成預設構造函數的。是以在繼承中,會出現以下問題:
class Animal { public: Animal(int weight, int height): //沒有提供無參構造函數 m_weight(weight), m_height(height) {} private: int m_weight; int m_height; }; class Dog: public Animal { public: Dog(int weight, int height, int type) { } private: int m_type; };
上面的子類和父類編譯會出錯。
因為子類Dog初始化之前要進行父類Animal的初始化,但是根據Dog的構造函數,沒有給父類傳遞參數,使用了父類Animal的無參構造函數。而父類Animal提供了有參構造函數,這樣編譯器就不會給父類Animal提供一個預設的無參數的構造函數了,是以編譯時報錯,說找不到合适的預設構造函數可用。要麼提供一個無參數的構造函數,要麼在子類的Dog的初始化清單中給父類Animal傳遞初始化參數,如下:
class Dog: public Animal { public: Dog(int weight, int height, int type): Animal(weight, height) { ; } private: int m_type; };
- 類中const常量,必須在初始化清單中初始,不能使用指派的方式初始化
class Dog: public Animal { public: Dog(int weight, int height, int type): Animal(weight, height), LEGS(4) //必須在初始化清單中初始化 { //LEGS=4; //error } private: int m_type; const int LEGS; };
- 包含有自定義資料類型(類)對象的成員初始化
class Food { public: Food(int type = ) { m_type = ; } Food(Food &other) //拷貝構造函數 { m_type = other.m_type; } Food & operator = (Food &other) //重載指派=函數 { m_type = other.m_type; return *this; } private: int m_type; }; //(1)構造函數指派方式,初始化成員對象m_food class Dog: public Animal { public: Dog(Food &food) { m_food = food; //初始化成員對象 } private: Food m_food; }; //使用 Food fd; Dog dog(fd); /*Dog dog(fd);結果是,先執行了對象類型構造函數Food(int typt = 10) 然後執行對象類型構造函數Food & operator = (Food &other)*/ //(2)構造函數初始化清單方式 class Dog: public Animal { public: Dog(Food &food) :m_food(food) //初始化 成員對象 { //m_food = food; } private: Food m_food; }; //使用 Food fd; Dog dog(fd); /*Dog dog(fd);結果是執行Food(Food &other)拷貝構造函數完成舒适化*/
初始化構造函數的執行過程:
1)傳參 2)給類成員開辟空間 3)執行冒号文法給資料成員初始化 4)執行構造函數體内内容
- 内部資料類型(char, int……指針等)
-
複制(拷貝)構造函數
複制構造函數參數為類對象本身的引用,用于根據一個已存在的對象複制出一個新的該類的對象,一般在函數中會将已存在對象的資料成員的值複制一份到新建立的對象中。
若沒有顯示的寫複制構造函數,則系統會預設建立一個,但當類中有指針成員時,由系統預設建立該複制構造函數會存在風險。
- 複制構造函數用于複制本類的對象
Student s2(, ); Student s3(s2); //将對象s2複制給s3。注意複制和指派的概念不同
- 下面這種情況叫做指派,不調用複制構造函數
Student s4; s4 = s2;
- 大多數時候,在類中我們沒有聲明複制構造函數,而系統會自動為我們生成一個
Student(Student &b) { this.x = b.x; this.y = b.y; }
- 複制構造函數用于複制本類的對象
-
轉換構造函數
轉換構造函數,根據一個指定的類型的對象建立一個本類的對象
例如:下面将根據一個double類型的對象建立一個Complex對象
Complex::Complex(double r) { m_real = r; m_imag = ; } Student(int r) { int num = ; int age = r; }
-
轉換構造函數用在哪裡?
加入重載了+号運算符,使得兩個Student類的對象可以相加,其結果為兩個對象的成員變量age之和
Student s1(,); Student s2(,); s1+s2;
-
那麼s1+19呢(類對象與int直接相加)?
因為我們定義了轉換構造函數,是以s1+19的執行過程:
首先調用+号運算符,發現19不是Student類的對象,而是int類型
然後調用轉換構造函數,将19變為Student(19)
現在便可以進行加法運算
-