08-初始化列表、父類構造函數、虛函數和多態

《C++文章匯總》
上一篇介紹了引用和匯編《07-內存管理、命名空間和繼承》,本文初始化列表、父類構造函數、虛函數和多態。

1.初始化列表:和構造函數的匯編代碼一樣

?特點
一種便捷的初始化成員變量的方式
只能用在構造函數中
初始化順序只跟成員變量的聲明順序有關

struct Person {
    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;
}
//輸出
18
180

初始化列表可以接收表達式和函數

int func(){return 8;}
struct Person {
    int m_age;
    int m_height;
    Person(int age,int height):m_age(func()),m_height(height+2){
        
    };
};
int main(){
    Person person(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
8
182

賦值順序變化,初始化只與成員變量的定義順序有關

struct Person {
    int m_age;
    int m_height;
     Person(int age,int height):m_age(m_height),m_height(height+2){
        
     };
};
int main(){
    Person person(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
32766
182

在構造函數中改變成員變量的賦值順序,結果并不會發生改變

struct Person {
    int m_age;
    int m_height;
    Person(int age,int height):m_height(height+2),m_age(m_height){
       
    };
};
int main(){
    Person person(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
32766
182

如果改變m_age和m_height的定義順序,結果會改變

struct Person {
    int m_height;
    int m_age;
    Person(int age,int height):m_height(height+2),m_age(m_height){
       
    };
};
int main(){
    Person person(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
182
182

通過函數調用能看到順序

int myAge(){
    cout << "myAge()" << endl;
    return 10;
};
int myHeight(){
    cout << "myHeight()" << endl;
    return 10;
};
struct Person {
    int m_age;
    int m_height;
    Person(int age,int height):m_height(myHeight()),m_age(myAge()){
    
    };
};
int main(){
    Person person(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
myAge()
myHeight()
10
10

2.初始化列表與默認參數配合使用:相當于寫了三個構造函數

struct Person {
    int m_age;
    int m_height;
    Person(int age = 1,int height = 125):m_age(age),m_height(height){
    
    };
};
int main(){
    Person person;
    Person person0();//函數聲明
    Person person1(17);
    Person person2(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    cout << person1.m_age << endl;
    cout << person1.m_height << endl;
    cout << person2.m_age << endl;
    cout << person2.m_height << endl;
    return 0;
}
//輸出
1
125
17
125
18
180

初始化列表函數中的賦值,會在自動生成的賦值語句后面執行

struct Person {
    int m_age;
    int m_height;
    Person(int age = 1,int height = 125):m_age(age),m_height(height){
        m_age = 10;
    };
};
int main(){
    Person person;
    Person person0();//函數聲明
    Person person1(17);
    Person person2(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    cout << person1.m_age << endl;
    cout << person1.m_height << endl;
    cout << person2.m_age << endl;
    cout << person2.m_height << endl;
    return 0;
}
//輸出
10
125
10
125
10
180

? 如果函數聲明和實現是分離的
初始化列表只能寫在函數的實現中
默認參數只能寫在函數的聲明中

struct Person {
    int m_age;
    int m_height;
    Person(int age = 1,int height = 125);
    };
Person::Person(int age,int height):m_age(age),m_height(height){
    m_age = 10;
};
int main(){
    Person person;
    Person person0();//函數聲明
    Person person1(17);
    Person person2(18,180);
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    cout << person1.m_age << endl;
    cout << person1.m_height << endl;
    cout << person2.m_age << endl;
    cout << person2.m_height << endl;
    return 0;
}
//輸出
10
125
10
125
10
180

3.構造函數的互相調用

構造函數之間的互相調用在初始化列表中,在構造函數Person()中會將外面的person對象傳入到Person(10,20)中,Person(10,20)中改變的m_age,m_height的值就為外面的Person對象的成員變量的值

struct Person {
    int m_age;
    int m_height;
    Person():Person(10,20){
        //創建了一個臨時的person對象
        Person(10,20);
//        Person person;
//        person.m_age = 10;
//        person.m_height = 20;
    }
    Person(int age,int height){
        m_age = age;
        m_height = height;
    }
};
int main(){
    Person person;
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
10
20

? 注意:下面的寫法是錯誤的,初始化的是一個臨時對象,傳入的Person(int age,int height)方法中對象的地址是臨時對象的地址,傳入匯編,并不會改變外面person對象的成員變量m_age和m_height的值

struct Person {
    int m_age;
    int m_height;
    Person(){
        //創建了一個臨時的person對象
        Person(10,20);
//        Person person;
//        person.m_age = 10;
//        person.m_height = 20;
    }
    Person(int age,int height){
        m_age = age;
        m_height = height;
    }
};
int main(){
    Person person;
    cout << person.m_age << endl;
    cout << person.m_height << endl;
    return 0;
}
//輸出
-272632816
32766

4.父類的構造函數

? 子類的構造函數默認會調用父類的無參構造函數

struct Person{
    int m_age;
    Person(){
        cout << "Person::Person()" << endl;
    }
};
struct Student:Person{
    int m_no;
    Student(){
        cout << "Student::Student()" << endl;
    }
};
int main(){
    Student student;
    return 0;
}
//輸出
Person::Person()
Student::Student()

? 如果子類的構造函數顯式地調用了父類的有參構造函數,就不會再去默認調用父類的無參構造函數

struct Person{
    int m_age;
    Person(){
        cout << "Person::Person()" << endl;
    }
    Person(int age){
        cout << "Person::Person(int age)" << endl;
    }
};
struct Student:Person{
    int m_no;
    Student():Person(10){
        cout << "Student::Student()" << endl;
    }
};
int main(){
    Student student;
    return 0;
}
//輸出
Person::Person(int age)
Student::Student() 

? 如果父類缺少無參構造函數,子類的構造函數必須顯式調用父類的有參構造函數

struct Person{
    int m_age;
    Person(int age){
        cout << "Person::Person(int age)" << endl;
    }
};
struct Student:Person{
    int m_no;
    Student():Person(10){
        cout << "Student::Student()" << endl;
    }
};
int main(){
    Student student;
    return 0;
}
//輸出
Person::Person(int age)
Student::Student()

? 父類沒有構造函數,就不調用父類構造函數了

struct Person{
    int m_age;
};
struct Student:Person{
    int m_no;
    Student(){
        cout << "Student::Student()" << endl;
    }
};
int main(){
    Student student;
    return 0;
}
//輸出
Student::Student()

? 父類子類成員變量同時初始化

class Person{
    int m_age;
public:
    Person(int age) :m_age(age){}
};
class Student:Person{
    int m_no;
public:
    Student(int no,int age) :m_no(no),Person(age){
        cout << "Student::Student()" << endl;
    }
};
int main(){
    Student student(10,20);
    return 0;
}

5.構造、析構順序

構造順序:先父類后子類
析構順序:先子類后父類

struct Person {
    Person(){
        cout << "Person::Person()" << endl;
    };
    ~Person(){
        cout << "Person::~Person()" << endl;
    };
};
struct Student:Person{
    Student(){
        cout << "Student::Student()" << endl;
    };
    ~Student(){
        cout << "Student::~Student()" << endl;
    };
};
int main(){
    {
      Student student;
    }
    return 0;
}
//輸出
Person::Person()
Student::Student()
Student::~Student()
Person::~Person()

6.多態

? 父類指針可以指向子類對象,是安全的,開發中經常用到(繼承方式必須是public)

struct Person {
    int m_age;
};
struct Student:Person{
    int m_score;
};
int main(){
    Person *p = new Student();
    p->m_age = 10;//安全區域
    getchar();
    return 0;
}

? 子類指針指向父類對象是不安全的:編譯不會報錯,編譯器只管是什么類型,運行時會報錯與否,與編譯器的容錯能力有關,Xcode會報錯error: 'm_score' is a private member of 'Student

struct Person {
    int m_age;
};
struct Student:Person{
    int m_score;
};
int main(){
//    Person *p = new Student();
//    p->m_age = 10;//安全區域
    Student *p = (Student *)new Person();
    p->m_age = 10;
    p->m_score = 99;//不安全,實質上p指針指向的地址Person對象里面并不存在m_score的區域,p->m_score指向的是未知區域,可能誤將別人的數據抹掉或篡改掉
    getchar();
    return 0;
}
image

若是class類,則繼承方式默認為private,父類的指針就不能指向子類的對象了,此時在父類前要加上public

class Person {
public:
    int m_age;
};
class Student: public Person{
    int m_score;
};
int main(){
    Person *p = new Student();
    p->m_age = 10;//安全區域
    getchar();
    return 0;
}

? 默認情況下,編譯器只會根據指針類型調用對應的函數,不存在多態
? 多態是面向對象非常重要的一個特性
同一操作作用于不同的對象,可以有不同的解釋,產生不同的執行結果
在運行時,可以識別出真正的對象類型,調用對應子類中的函數

struct Animal{
    void speak(){
        cout << "Animal::speak()" << endl;
    }
    void run(){
        cout << "Animal::run()" << endl;
    }
};
struct Dog:Animal{
    void speak(){
        cout << "Dog::speak()" << endl;
    }
    void run(){
        cout << "Dog::run()" << endl;
    }
};
struct Cat:Animal{
    void speak(){
        cout << "Cat::speak()" << endl;
    }
    void run(){
        cout << "Cat::run()" << endl;
    }
};
struct Pig:Animal{
    void speak(){
        cout << "Pig::speak()" << endl;
    }
    void run(){
        cout << "Pig::run()" << endl;
    }
};
void liu(Animal *p){
    p->speak();
    p->run();
}
int main(){
    liu(new Dog());
    liu(new Cat());
    liu(new Pig());
    
    Cat *p = (Cat *)new Dog();
    p->speak();//call Cat::speak
    p->run();//call Cat::run
    return 0;
}
//輸出
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Cat::speak()
Cat::run()

查看匯編發現,在調用函數時會直接在函數面前加上指針類型,指針類型為Animal *,故全部打印為Animal,那么C++如何實現多態呢?
? 多態的要素
子類重寫父類的成員函數(override)
父類指針指向子類對象
利用父類指針調用重寫的成員函數

7.虛函數

? C++中的多態通過虛函數(virtual function)來實現
? 虛函數:被virtual修飾的成員函數
? 只要在父類中聲明為虛函數,子類中重寫的函數也自動變成虛函數(也就是說子類中可以省略virtual關鍵字)

struct Animal{
    virtual void speak(){
        cout << "Animal::speak()" << endl;
    }
    virtual void run(){
        cout << "Animal::run()" << endl;
    }
};
struct Dog:Animal{
    void speak(){
        cout << "Dog::speak()" << endl;
    }
    void run(){
        cout << "Dog::run()" << endl;
    }
};
struct Cat:Animal{
    void speak(){
        cout << "Cat::speak()" << endl;
    }
    void run(){
        cout << "Cat::run()" << endl;
    }
};
struct Pig:Animal{
    void speak(){
        cout << "Pig::speak()" << endl;
    }
    void run(){
        cout << "Pig::run()" << endl;
    }
};
void liu(Animal *p){
    p->speak();
    p->run();
}
int main(){
    liu(new Dog());
    liu(new Cat());
    liu(new Pig());
    
    Cat *p = (Cat *)new Dog();
    p->speak();//call Cat::speak
    p->run();//call Cat::run
    return 0;
}
//輸出
Dog::speak()
Dog::run()
Cat::speak()
Cat::run()
Pig::speak()
Pig::run()
Dog::speak()
Dog::run()
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容