C++面向?qū)ο蟮睦^承與多態(tài)

[toc]

繼承

? 繼承,可以讓子類擁有父類的所有成員(變量\函數(shù))

image-20210403150553278

對(duì)象的內(nèi)存布局

struct Person {

};

struct Student : Person {
    int m_no;
};

struct GoodStudent : Student {
    int m_money;
};

? 父類的成員變量在前,子類的成員變量在后

image-20210403151255004

成員訪問權(quán)限

? 成員訪問權(quán)限、繼承方式有3種

  • public:公共的,任何地方都可以訪問(struct默認(rèn))

  • protected:子類內(nèi)部、當(dāng)前類內(nèi)部可以訪問

  • private:私有的,只有當(dāng)前類內(nèi)部可以訪問(class默認(rèn))

? 子類內(nèi)部訪問父類成員的權(quán)限,是以下2項(xiàng)中權(quán)限最小的那個(gè)

  • 成員本身的訪問權(quán)限

  • 上一級(jí)父類的繼承方式

? 開發(fā)中用的最多的繼承方式是public,這樣能保留父類原來的成員訪問權(quán)限

? 訪問權(quán)限不影響對(duì)象的內(nèi)存布局

struct Person {
private:
   int m_age;

public:
   void setAge(int age) {
      m_age = age;
   }

   int getAge() {
      return m_age;
   }
};

struct Student : public Person {

};

struct GoodStudent : public Student {
   void work() {

   }
};
class Person {
private:
   int m_age;
public:
   void run() {

   }
};

struct Student : public Person {

};

class GoodStudent : public Student {

};

初始化列表

? 特點(diǎn)

  • 一種便捷的初始化成員變量的方式

  • 只能用在構(gòu)造函數(shù)中

  • 初始化順序只跟成員變量的聲明順序有關(guān)

struct Person {
   int m_age;
   int m_height;

   /*Person(int age, int height) {
      m_age = age;
      m_height = height;
   }*/

   // 語法糖
   Person(int age = 0, int height = 0) :m_age(age), m_height(height) {}
};
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_age(myAge()), m_height(myHeight()){
        
    }
};
int main() {
    Person person(18, 180);

    cout << person.m_age << endl;
    cout << person.m_height << endl;
}

myAge()
myHeight()
10
10

  • 初始化順序只跟成員變量的聲明順序有關(guān)
image-20210405132725878
image-20210405132742522

說明輸出和定義在類中成員的順序有關(guān),和代碼Person(int age , int height):m_age(myAge()), m_height(myHeight())定義的順序無關(guān)。

初始化列表與默認(rèn)參數(shù)配合使用

? 如果函數(shù)聲明和實(shí)現(xiàn)是分離的

  • 初始化列表只能寫在函數(shù)的實(shí)現(xiàn)中

  • 默認(rèn)參數(shù)只能寫在函數(shù)的聲明中

struct Person {
   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 person1;
    Person person2(17);
    Person person(18, 180);
}

構(gòu)造函數(shù)的互相調(diào)用

image-20210405134229541
struct Person {
    int m_age;
    int m_height;

    /*Person() :Person(10, 20) {

    }*/

    Person() :Person(10, 20) {
        // 創(chuàng)建了一個(gè)臨時(shí)的Person對(duì)象
        // Person(10, 20);
        /*Person person;
        person.m_age = 10;
        person.m_height = 20;*/
    }

    Person(int age, int height) {
        this->m_age = age;
        this->m_height = height;
    }
};

int main() {

    Person person;
    cout << person.m_age << endl;
    cout << person.m_height << endl;

    return 0;
}

父類的構(gòu)造函數(shù)

? 子類的構(gòu)造函數(shù)默認(rèn)會(huì)調(diào)用父類的無參構(gòu)造函數(shù)

? 如果子類的構(gòu)造函數(shù)顯式地調(diào)用了父類的有參構(gòu)造函數(shù),就不會(huì)再去默認(rèn)調(diào)用父類的無參構(gòu)造函數(shù)

? 如果父類缺少無參構(gòu)造函數(shù),子類的構(gòu)造函數(shù)必須顯式調(diào)用父類的有參構(gòu)造函數(shù)

struct Person {
   Person() {
      cout << "Person::Person()" << endl;
   }
   ~Person() {
      cout << "Person::~Person()" << endl;
   }
};

struct Student : Person {
   Student() {
      // call Person::Person
      cout << "Student::Student()" << endl;
   }
   ~Student() {
      cout << "Student::~Student()" << endl;
      // call Person::~Person
   }
};
int main() {

   {
      Student student;
   }

   // Student student(18, 34);

   return 0;
}
/*
Person::Person()
Student::Student()
Student::~Student()
Person::~Person()
*/
  • 子類的構(gòu)造函數(shù)默認(rèn)會(huì)調(diào)用父類的無參構(gòu)造函數(shù)
image-20210405135555425

構(gòu)造、析構(gòu)順序

image-20210405140202765

多態(tài)

? 默認(rèn)情況下,編譯器只會(huì)根據(jù)指針類型調(diào)用對(duì)應(yīng)的函數(shù),不存在多態(tài)

? 多態(tài)是面向?qū)ο蠓浅V匾囊粋€(gè)特性

  • 同一操作作用于不同的對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果

  • 在運(yùn)行時(shí),可以識(shí)別出真正的對(duì)象類型,調(diào)用對(duì)應(yīng)子類中的函數(shù)

? 多態(tài)的要素

  • 子類重寫父類的成員函數(shù)(override

  • 父類指針指向子類對(duì)象

  • 利用父類指針調(diào)用重寫的成員函數(shù)

父類指針、子類指針

image-20210405140630028
struct Person {
   int m_age;
};

struct Student : public Person {
   int m_score;
};

void test() {
   // 父類指針 指向 子類對(duì)象
// Person *p = new Student();
// p->m_age = 10;

   Student *p = (Student *) new Person();
   p->m_age = 10;
   p->m_score = 100;//危險(xiǎn),動(dòng)了age后面的4個(gè)字節(jié),修改了非自己內(nèi)存的數(shù)據(jù)
}
struct Animal {
   virtual void speak() {
      cout << "Animal::speak()" << endl;
   }
   virtual void run() {
      cout << "Animal::run()" << endl;
   }
};

struct Dog : Animal {
   // 重寫(覆寫、覆蓋、override)
   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;
   }
};
  • 同一操作作用于不同的對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果
//同一操作作用于不同的對(duì)象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果
void liu(Animal *p) {
   p->speak();
   p->run();
}

int main() {

    liu(new Dog());
    liu(new Cat());
    liu(new Pig());
    

    return 0;
}
/*
Dog::speak()
Dog::run()
Cat::speak()
Cat::run()
Pig::speak()
Pig::run()
*/
  • 默認(rèn)情況下,編譯器只會(huì)根據(jù)指針類型調(diào)用對(duì)應(yīng)的函數(shù),不存在多態(tài)
struct Animal {
   void speak() {
      cout << "Animal::speak()" << endl;
   }
   void run() {
      cout << "Animal::run()" << endl;
   }
};
/*
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()
*/
**不存在多態(tài)**

虛函數(shù)

? C++中的多態(tài)通過虛函數(shù)(virtual function)來實(shí)現(xiàn)

? 虛函數(shù):被virtual修飾的成員函數(shù)

? 只要在父類中聲明為虛函數(shù),子類中重寫的函數(shù)也自動(dòng)變成虛函數(shù)(也就是說子類中可以省略virtual關(guān)鍵字

struct Animal {
    virtual void speak() {
        cout << "Animal::speak()" << endl;
    }
    virtual void run() {
        cout << "Animal::run()" << endl;
    }
};
int main() {
   Animal *p = new Pig();
   p->speak();
   p->run();
}
/*
Pig::speak()
Pig::run()
*/
虛函數(shù)

虛表

? 虛函數(shù)的實(shí)現(xiàn)原理是虛表,這個(gè)虛表里面存儲(chǔ)著最終==需要調(diào)用的虛函數(shù)地址==,這個(gè)虛表也叫虛函數(shù)表

image-20210405144422323
struct Animal {
    int m_age;
    void speak() {
        cout << "Animal::speak()" << endl;
    }
    void run() {
        cout << "Animal::run()" << endl;
    }
};

struct Cat : Animal {
    int m_life;
    void speak() {
        cout << "Cat::speak()" << endl;
    }
    void run() {
        cout << "Cat::run()" << endl;
    }
};

int main() {

    cout<< sizeof (Cat)<<endl;
}
    

8

struct Animal {
    int m_age;
    virtual void speak() {
        cout << "Animal::speak()" << endl;
    }
    void run() {
        cout << "Animal::run()" << endl;
    }
};

virtual void speak()

cout<< sizeof (Cat)<<endl;

16

image-20210405145641286

虛表里面存儲(chǔ)著最終需要調(diào)用的虛函數(shù)地址,這個(gè)虛表也叫虛函數(shù)表

(x86環(huán)境的圖)

x86環(huán)境的圖
int main() {


    Animal *cat = new Cat();
    cat->m_age = 20;
    cat->speak();
    cat->run();
}

下面我們通過上述代碼的內(nèi)存數(shù)據(jù)及匯編指令,來窺探虛表的原理

cat堆內(nèi)存數(shù)據(jù)

image-20210405152135545
image-20210405152143497
image-20210405152904006
image-20210405152915125
image-20210405154238056
image-20210405154515650

總結(jié)一下Animal類由于存在虛函數(shù)virtual void speak()virtual void run() 所以Animal對(duì)象創(chuàng)建完成后,前8個(gè)字節(jié)是一個(gè)地址指針,指向了一個(gè)虛表的地址

image-20210405154943917

由于有兩個(gè)虛函數(shù),所以指向的地址,前8位speak函數(shù)的地址,后8個(gè)字節(jié)為run函數(shù)的地址。

Animal 虛表地址之后,跟著為屬性成員的地址,movl $0x14, 0x8(%rax) 修改m_age的內(nèi)存地址的值為20.

0x10fe79035 <+85>: callq *(%rcx)0x10fe79041 <+97>: callq *0x8(%rcx)都是間接調(diào)用,先找到虛表地址,從內(nèi)存地址中,取出函數(shù)地址間接調(diào)用到對(duì)應(yīng)類對(duì)象真實(shí)實(shí)現(xiàn)函數(shù)的地址上。

調(diào)用父類的成員函數(shù)實(shí)現(xiàn)

image-20210405162029567
image-20210405162356201
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容