文章目錄
構造函數1(Constructor)
構造函數(也叫構造器), 在對象創建的時候自動調用, 一般用於完成對象的初始化工作.
特點:
(1)函數名與類同名, 無返回值(void都不能寫), 可以有參數, 可以重載, 可以有多個構造函數.
(2)一旦自定義了構造函數, 必須用其中一個自定義的構造函數來初始化對象.
注意:
(1)通過malloc分配的對象不會調用構造函數
(2)通過new分配的對象可以調用構造函數, 即在堆空間中的對象可以調用構造函數, 在棧空間的也可以, 在全局區(數據段)的也可以.只有malloc不會調用.因爲malloc在C語言就有了, 那時候還沒有構造函數.
所以new至少比malloc多做了調用構造函數這件事情.
(3)一個廣爲流傳的, 很多教程\書籍都推崇的錯誤結論:
默認情況下, 編譯器會爲每一個類生成空的無參的構造函數.
正確理解: 在某些特定的情況下, 編譯器纔會爲類生成空的無參的構造函數.
構造函數2
構造函數的調用:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person() {
m_age = 0;
cout << "Person()" << endl;
}
Person(int age) {
m_age = age;
cout << "Person(int age)" << endl;
}
};
Person person1(); // 函數聲明
Person person1() { // 函數實現
return Person();
}
Person g_person0; // Person();
Person g_person1(); // 全局的函數聲明
// 返回值 函數名
Person g_person2(10); // Person(int)
int main()
{
Person person0; // Person()
Person person1(); // 局部的函數聲明
Person person2(20); // Person(int)
Person *p0 = new Person; // Person()
Person *p1 = new Person(); // Person()
Person *p2 = new Person(30); // personn(int)
return 0;
}
成員變量的初始化(瞭解)
如果自定義了構造函數, 除了全局區, 其他內存空間的成員變量默認都不會被初始化, 需要開發人員手動初始化.
如果有很多個成員變量, 如何方便的初始化.
class Person {
int m_age1;
int m_age2;
int m_age3;
int m_age4;
int m_age5;
Person() {
memset(this, 0, sizeof(Person));
// 從person對象的地址開始, 它的所有字節都清0
}
};
析構函數(Destructor)
析構函數(也叫析構器), 在對象銷燬(對象的內存被回收的時候)的時候自動調用, 一般用於完成對象的清理工作.
特點:
(1)函數名以~開頭, 與類同名, 無返回值(void都不能寫), 無參, 不可以重載, 有且僅有一個析構函數.
注意:
(1)通過malloc分配的對象free的時候不會調用析構函數.
(2)構造函數, 析構函數要聲明爲public, 才能被外界正常使用.所以構造函數和析構函數必須聲明爲public, 否則會報錯.
繼承
繼承, 可以讓子類擁有父類的所有成員(變量\函數)
成員訪問權限
成員訪問權限, 繼承方式有3種
public : 公共的, 任何地方都可以訪問(struct默認)
protected: 子類內部, 當前類內部可以訪問
private: 私有的, 只有當前類內部可以訪問(class默認)
結論:
子類內部訪問父類成員的權限, 是以下2項中權限最小的那個
1.成員本身的訪問權限
2.上一級父類的繼承方式
(1)所以一般都寫public繼承, 因爲public繼承可以將父類原有的訪問權限繼承下來.
(2)訪問權限不影響對象的內存佈局.
初始化列表1
特點:
(1)一種便捷的初始化成員變量的方式
(2)只能用在構造函數中
(3)初始化順序只跟成員變量的聲明順序有關
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
/*Person(int age, int height) {
m_age = age;
m_height = height;
}*/
// 語法糖
Person(int age, int height) : m_age(age), m_height(height) {
}
// 這個構造函數與上面的構造函數等價
};
int main()
{
Person person(18, 180);
cout << person.m_age << endl;
cout << person.m_height << endl;
return 0;
}
初始化列表與默認參數配合使用
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person(int age = 0, int height = 0) : m_age(age), m_height(height) {
}
// 一個構造函數相當於寫了3個構造函數
};
int main()
{
Person person1;
Person person2(19);
Person person3(20, 180);
return 0;
}
如果函數聲明和實現是分離的
初始化列表只能寫在函數的實現中
默認參數只能寫在函數聲明中
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person(int age = 0, int height = 0);
};
Person::Person(int age, int height) : m_age(age), m_height(height) {
}
int main()
{
Person person;
return 0;
}
構造函數的互相調用
結論:構造函數調用構造函數要在初始化列表裏面寫.
例如:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
int m_height;
Person() : Person(0, 0) {}
Person(int age, int height) : m_age(age), m_height(height) {}
};
int main()
{
Person person;
return 0;
}
注意:下面的寫法是錯誤的, 初始化的是一個臨時對象.
class Person {
int m_age;
int m_height;
Person() {
Person(0, 0);
}
Person(int age, int height) : m_age(age), m_height(height) {}
};
初始化列表3-父類的構造函數
(1)子類的構造函數默認會調用父類的無參構造函數
(2)如果子類的構造函數顯式地調用了父類的有參構造函數, 就不會再去默認調用父類的無參構造函數
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person() {
cout << "Person::Person()" << endl;
}
Person(int age) {
cout << "Person::Person(int age)" << endl;
}
};
class Student : public Person {
public:
int m_no;
Student() : Person(10) {
cout << "Student::Student()" << endl;
}
};
int main()
{
Student student;
return 0;
}
(3)如果父類缺少無參構造函數(但有有參構造函數), 子類的構造函數必須顯式調用父類的有參構造函數.不然會報錯. 因爲子類的構造函數默認會調用父類無參的構造函數, 如果父類缺少無參的構造函數, 就報錯.
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
Person(int age) {
cout << "Person::Person(int age)" << endl;
}
};
class Student : public Person {
public:
int m_no;
Student() : Person(10) {
cout << "Student::Student()" << endl;
}
//父類缺少無參的構造函數, 子類的構造函數
//必須顯式的調用父類的有參構造函數.
};
int main()
{
Student student;
return 0;
}
(4)如果父類什麼構造函數都沒有, 那子類的構造函數就不調用了.
(5)價值:
#include <iostream>
using namespace std;
class Person {
private:
int m_age;
public:
Person(int age) : m_age(age){ }
int getAge() {
return m_age;
}
};
class Student : public Person {
private:
int m_no;
public:
Student(int age, int no) : Person(age), m_no(no) { }
int getNo() {
return m_no;
}
};
int main()
{
Student student(10, 20);
cout << student.getAge() << '\n' <<
student.getNo() << endl;
return 0;
}
其他C++系列文章:
C++知識點總結(基礎語法1-函數重載, 默認參數)
C++知識點總結(基礎語法2-內聯函數, const, 引用)
C++知識點總結(面向對象1-類和對象, this指針, 內存佈局)
C++知識點總結(面向對象2-構造函數, 初始化列表)C++知識點總結(面向對象4-多繼承, 靜態成員static)
C++知識點總結(面向對象5-const成員, 拷貝構造函數)
C++知識點總結(面向對象6-隱式構造, 友元, 內部類, 局部類)
C++知識點總結(其他語法1-運算符重載)
C++知識點總結(其他語法2-模板, 類型轉換, C++11新特性)