父類和子類指針
1 父類指針可以指向子類對象,這是安全的,開發(fā)中經(jīng)常用到,繼承方式必須是public方式。
2 子類指針指向父類是不安全的,因為子類指針可能訪問到父類以外的數(shù)據(jù),而子類對象并沒有創(chuàng)建。
class Person {
public:
int m_age;
};
class Student : public Person {
public:
int m_score;
};
int main() {
//父類指針指向子類對象,安全
Person *p = (Person *) new Student();
p->m_age = 10;
//子類指針指向父類對象,不安全
Student *s = (Student *) new Person();
//指向父類對象的子類指針訪問了自己的成員變量,指針指向超出了范圍
s->m_score = 10;
return 0;
}
多態(tài)
多態(tài)是面向?qū)ο笠粋€非常重要的特性,同一操作作用于不同的對象,產(chǎn)生不同的執(zhí)行結(jié)果,在運行時,可以識別出真正的對象類型,調(diào)用對應(yīng)子類的函數(shù)。產(chǎn)生多態(tài)的條件如下:子類重寫父類的成員函數(shù),父類指針指向子類對象,利用父類指針調(diào)用重寫的成員函數(shù),這個成員函數(shù)必須是由Virtual修飾的成員函數(shù),父類只要聲明了Virtual,子類自動轉(zhuǎn)為Virtual函數(shù)
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
};
class Dog : public Animal {
public:
void run() {
cout << "Dog::run()" << endl;
}
};
class ErHa : public Dog {
public:
void run() {
cout << "ErHa::run()" << endl;
}
};
int main() {
Dog *dog0 = new Dog();
dog0->run(); //調(diào)用dog下的run函數(shù)
Dog *dog1 = new ErHa();
dog1->run();//調(diào)用ErHa下的run函數(shù)
return 0;
}
以上就是虛函數(shù)實現(xiàn)的列子,我們再進(jìn)一步,為什么不加Virtual 就不能實現(xiàn)多態(tài)了,加了Virtual 就能實現(xiàn)多態(tài)呢,多態(tài)的實現(xiàn)是靠虛表實現(xiàn)的。我們先看看這幾個類的大小
//8
cout << sizeof(Dog) << endl;
//8
cout << sizeof(ErHa) << endl;
//8
cout << sizeof(Animal) << endl;
這幾個類sizeof大小是8,我們這是64位的,其實這個新增加的大小就是虛表的地址,指向虛表,而且這個虛表地址在類的最前面,而虛表里面存著本類虛函數(shù)的地址,從而進(jìn)行真正的調(diào)用,每一個類只有一份虛表,多個對象共享一份虛表,無論這個對象是在堆還是棧還是全局對象。我們可以通過反匯編和內(nèi)存調(diào)試也能看出來,這里我就不演示了。當(dāng)父類實現(xiàn)了虛函數(shù),而子類沒有實現(xiàn)該虛函數(shù)的時候,我們來看看這個情況:
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
virtual void speak() {
cout << "Animal::speak()" << endl;
}
};
class Dog : public Animal {
public:
int m_age; //dog的age
};
int main() {
Animal *animal = new Animal();
animal->run();會調(diào)用Animal::run()
animal->speak();會調(diào)用Animal::speak()
Dog *dog0 = new Dog();
dog0->run(); //會調(diào)用Animal::run()
dog0->speak(); //會調(diào)用Animal::speak()
return 0;
}
當(dāng)子類沒有重寫父類虛函數(shù)的時候,它也會調(diào)用父類的虛函數(shù),其底層實現(xiàn)也是通過虛表查找到函數(shù)調(diào)用,也就是子類即時沒有虛函數(shù),也有自己的虛表,我反匯編看到此時父類子類的虛表地址一樣,可能不同的平臺有不同的處理,虛表沒有繼承一說。如果子類想調(diào)用父類的虛函數(shù)方法的時候,應(yīng)該顯示調(diào)用,注意C++ 沒有super等類似關(guān)鍵字,正確調(diào)用如下:
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
virtual void speak() {
cout << "Animal::speak()" << endl;
}
};
class Dog : public Animal {
public:
int m_age; //dog的age
void run() {
Animal::run(); //直接用類::顯示調(diào)用
cout << "Dog::run()" << endl;
}
void speak() {
Animal::speak(); //直接用類::顯示調(diào)用
cout << "Dog::speak()" << endl;
}
};
int main() {
Dog *dog0 = new Dog();
dog0->run();
dog0->speak();
return 0;
}
含有虛虛函數(shù)實現(xiàn)的父類時候,父類的析構(gòu)函數(shù)也需要聲明為virtual函數(shù),此時析構(gòu)函數(shù)變成了虛析構(gòu)函數(shù),這樣才能夠保證銷毀對象的時候父類子類析構(gòu)函數(shù)調(diào)用,保證析構(gòu)的完整性,子類可不加(virtaul)。
純虛函數(shù)
沒有函數(shù)體,且初始化為0的虛函數(shù),用來定義接口規(guī)范
class Animal {
public:
virtual void speak() = 0;
virtual void run() = 0;
};
含有純虛函數(shù)的類是抽象類,不可以實例化,不能創(chuàng)建對象,抽象類成也可以包含其它非純虛函數(shù),以及其他成員變量,抽象類的指針可以指向子類對象,如果父類是抽象類,子類沒有完全實現(xiàn)純虛函數(shù),那么這個子類依然是抽象類
多繼承
C++允許一個類繼承多個類,可以擁有多個類的特性,多繼承增加了設(shè)計的復(fù)雜性,不建議使用。
#include <iostream>
using namespace std;
class Student {
public:
int m_score;
Student(int score = 0) :m_score(score) { }
void study() {
cout << "Student::study() - score = " << m_score << endl;
}
~Student() {
cout << "~Student" << endl;
}
};
class Worker {
public:
int m_salary;
Worker(int salary = 0) :m_salary(salary) { }
void work() {
cout << "Worker::work() - salary = " << m_salary << endl;
}
~Worker() {
cout << "~Worker" << endl;
}
};
class Undergraduate : public Student, public Worker {
public:
int m_grade;
Undergraduate(
int score = 0,
int salary = 0,
int grade = 0) :Student(score), Worker(salary), m_grade(grade) {
}
void play() {
cout << "Undergraduate::play() - grade = " << m_grade << endl;
}
~Undergraduate() {
cout << "~Undergraduate" << endl;
}
};
int main() {
{
Undergraduate ug;
ug.m_score = 100;
ug.m_salary = 2000;
ug.m_grade = 4;
ug.study();
ug.work();
ug.play();
}
cout << sizeof(Undergraduate) << endl;
return 0;
}
注意:這種多繼承,父類的成員變量在子類前面,先繼承誰,誰的成員就在前面。多繼承的構(gòu)造函數(shù)一樣需要使用初始化列表調(diào)用父類構(gòu)造函數(shù),
父類如果都含有虛函數(shù),子類多繼承多個父類后,子類對象會產(chǎn)生多個虛函數(shù)表,順序跟繼承順序有關(guān)。
#include <iostream>
using namespace std;
class Student {
public:
virtual void study() {
cout << "Student::study()" << endl;
}
};
class Worker {
public:
virtual void work() {
cout << "Worker::work()" << endl;
}
};
class Undergraduate : public Student, public Worker {
public:
void study() {
cout << "Undergraduate::study()" << endl;
}
void work() {
cout << "Undergraduate::work()" << endl;
}
void play() {
cout << "Undergraduate::play()" << endl;
}
};
int main() {
//含有16個字節(jié),因為有2張?zhí)摫? cout << sizeof(Undergraduate) << endl;
Student *stu = new Undergraduate();
stu->study();
Worker *worker = new Undergraduate();
worker->work();
return 0;
}
同名成員
C++允許同名成員函數(shù),同名成員變量,子類不會覆蓋,訪問的時候加上類名表示作用域,如下所示:
#include <iostream>
using namespace std;
class Student {
public:
int m_age;
};
class Worker {
public:
int m_age;
};
class Undergraduate : public Student, public Worker {
public:
int m_age;
};
int main() {
Undergraduate ug;
ug.m_age = 10;
ug.Student::m_age = 20;
ug.Worker::m_age = 30;
ug.Undergraduate::m_age = 40;
//這里等于12
cout << sizeof(Undergraduate) << endl;
return 0;
}
虛繼承
虛繼承是為了解決菱形繼承帶來的成員變量冗余,重復(fù)。而且最底層子類因為二義性無法訪問基類的的成員變量。我們先來看看菱形繼承:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
};
class Student : public Person {
public:
int m_score;
};
class Worker : public Person {
public:
int m_salary;
};
class Undergraduate : public Student, public Worker {
public:
int m_grade;
};
int main() {
Undergraduate ug;
ug.m_grade = 10;
ug.m_score = 20;
ug.Student::m_age = 20;
ug.Worker::m_age = 30;
cout << sizeof(Undergraduate) << endl; //20
return 0;
}
這里我們看到Undergraduate的大小是20,因為這樣繼承Undergraduate的父類兩個類都有m_age成員變量,而且訪問的時候我們需要通過作用域去訪問,直接訪問會報錯。這種繼承方式,基類的成員變量在最底層子類就冗余了,沒有必要,為了解決這種問題,可以使用虛繼承。加上virtual關(guān)鍵字:
#include <iostream>
using namespace std;
class Person {
public:
int m_age;
};
class Student :virtual public Person {
public:
int m_score;
};
class Worker :virtual public Person {
public:
int m_salary;
};
class Undergraduate : public Student, public Worker {
public:
int m_grade;
};
class Person1
{
};
int main() {
Undergraduate ug;
ug.m_grade = 10;
ug.m_score = 20;
ug.m_age = 30;
return 0;
}
此時三個類比如:Student、Worker、Undergraduate都有了虛函數(shù)表指針,此時成員變量m_age在最底層子類只有一份內(nèi)存。此時Person類被稱為虛基類。