菱形继承
- 当菱形继承时,两个父类拥有相同的数据,需要加作用域区分。
- 菱形继承带来的主要问题是子类继承两份相同的数据导致资源浪费。利用虚继承可以解决菱形继承问题。
class <派生类名>:virtual <继承方式><共同基类名> {
}
多态(父类指针或引用接住子类对象,运行时呈现子类的状态)
-
静态多态(地址早绑定,在编译阶段确定函数地址)
-
函数重载-----(**1.**返回值可同可不同,仅返回值不同不算重载 **2.**函数名必须相同 3.参数列表不同,包括个数不同,类型不同,参数排列顺序不同++++重载的函数经过编译器处理重新命名(根据作用域,函数名,参数类型列表命名)本质上还是不同的函数 ,占用不同的内存和入口地址++++)------ 和 运算符重载,复用函数名和运算符
//算重载 int add(int a, int *b){ cout << "add1" << endl; return a + *b; } int add(int a, const int *b){ cout << "add2" << endl; return a + *b; } //不算重载,报错 int add(int a, int b){ cout << "add1" << endl; return a + b; } int add(int a, const int b){ cout << "add2" << endl; return a + b; }
-
-
动态多态(地址晚绑定,运行阶段确定函数地址(注意:函数的默认参数是静态绑定,如果子类继承重写,默认参数写的和基类不一样,也是以基类的默认参数为准))满足条件
- 有继承关系
- 子类重写(函数返回值,函数名,参数列表完全一样)父类虚函数(父类里有一个vfptr虚函数表指针,指向一个虚函数表,表里面放了虚函数的地址,当子类继承父类时,同时继承了的虚函数表指针,重写父类虚函数时子类的虚函数地址在虚函数表里覆盖父类虚函数地址)
-
动态多态使用
- 父类的指针或引用指向子类对象(为什么要父类的指针或引用而不是子类的----因为一开始并不知道调用者指定的具体子类对象,多态是根据传入对象不同动态调用公共的接口)
/*
定义一个基类B0,有一个数据成员b0,一个显示数据成员的函数void display(),
定义继承自B0的类B1,有一个数据成员b1,一个显示其所有数据成员的函数void display(),
定义继承自B1的类D1,有一个数据成员d1,一个显示其所有数据成员的函数void display()。
在main函数中,分别定义三个类的对象,通过给基类指针p和基类引用r指向不同类型的对象,
实现display的调用。查看其调用的是哪个类的display函数。把display定义为虚函数,查看其运行情况。
*/
#include <iostream>
using namespace std;
class Base0{
private:
int data_b0;
public:
virtual void display(){
cout << "Base0::data_b0 " << data_b0 << endl;
}
};
class Base1:public Base0{
private:
int data_b1;
public:
virtual void display(){
Base0::display();
cout << "Base1::data_b1 " << data_b1 << endl;
}
};
class Derived1:public Base1{
private:
int data_d1;
public:
void display(){
Base1::display();
cout << "Derived1::data_d1 " << data_d1 << endl;
}
};
int main(){
//分别定义三个类的对象,
Base0 b;
Base1 bb;
Derived1 d;
//通过给基类指针p和基类引用r指向不同类型的对象,实现display的调用。
//查看其调用的是哪个类的display函数。
// Base0 *p = &b;
// p->display();
// cout << "------" << endl;
// p = &bb;
// p->display();
// cout << "------" << endl;
// p = &d;
// p->display();
// cout << "------" << endl;
Base0 &pp = b;
pp.display();
Base0 &ppp = bb;
ppp.display();
Base0 &pppp = d;
pppp.display();
//把display定义为虚函数,查看其运行情况
return 0;
}
纯虚函数
多态中,通常父类中的虚函数的实现是毫无意义的(但是定义一个函数为虚函数,不代表函数为不被实现的函数,虚函数必须实现,纯虚函数则一定没有定义),主要都是调用子类重写的内容,这时候可以将虚函数改为纯虚函数(virtual 返回值类型 函数名(参数列表)= 0)
此时父类变成抽象类,无法实例化对象(其实就是函数没实现,无法调用,所以无法实例化;可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。),子类如果没有重写父类中的纯虚函数,也会变成抽象类,无法实例化对象,所以子类一定要记得重写父类中的纯虚函数。
虚析构和纯虚析构(有继承关系,又要释放子类堆区内存)
相同点:
-
父类指针释放子类对象‘,delete 父类指针(可以调用到子类的析构函数)
- 必须具体实现函数
区别:
- 写成纯虚析构,该类属于抽象类,无法实例化对象
/*
定义animal类,数据成员有一个char* name,一个类bird派生自animal,一个数据成员char* sex。
1.分别实现两个类的构造和析构函数。
2.在main函数中定义基类指针指向派生类对象,验证析构函数的执行情况。
*/
#include <iostream>
#include <string.h>
using namespace std;
class Animal{
private:
char *name;
public:
Animal(char *name = NULL){
if (name == NULL){
this->name = NULL;
}else{
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
}
cout << "Animal 构造" << endl;
}
virtual ~Animal(){
delete[] name;
cout << "Animal 析构" << endl;
}
};
class Bird:public Animal{
private:
char *sex;
public:
Bird(char *sex = NULL){
if (sex == NULL){
this->sex = NULL;
}else{
this->sex = new char[strlen(sex) + 1];
strcpy(this->sex, sex);
}
cout << "Bird 构造" << endl;
}
~Bird(){
delete[] sex;
cout << "Bird 析构" << endl;
}
};
int main(){
Animal *p = new Bird;
delete p;
return 0;
}
/*
定义一个抽象基类B0, 一个void display(void)纯虚函数,
定义继承自B0的类B1,重定义函数display,
定义继承自B0的类D1,新定义一个函数 void display(int),
有一个int形参,然后再重定义无参的函数display。
(1)在main函数中,分别定义三个类的对象,通过给基类指针p和基类引用r指向不同类型的对象,
实现无参的display调用。查看其调用的是哪个类的函数。
(2)使r与D1的对象进行绑定,使用r能否调用有一个int形参的display函数,
如果不能调用 ,如何解决?
*/
#include <iostream>
using namespace std;
class Base0{
private:
int data_b0;
public:
virtual void display(void) = 0;
virtual void display(int n){
cout << "dfd" << endl;
}
};
class Base1:public Base0{
private:
int data_b1;
public:
void display(){
cout << "Base1::data_b1 " << data_b1 << endl;
}
};
class Derived1:public Base0{
private:
int data_d1;
public:
void display(int n){
cout << n << endl;
}
void display(void){
cout << "Derived1::data_d1 " << data_d1 << endl;
}
};
int main(){
// Base0 a; //抽象类只能用作其他类的基类,不能建立抽象类对象。
Base1 aa;
Derived1 aaa;
Base0 *p = NULL;
p = &aa; //可以定义指向抽象类的指针。
p->display();
p = &aaa;
p->display();
Base0 &rr = aa;//可以定义指向抽象类的引用。
rr.display();
Base0 &rrr = aaa;
rrr.display(1);
rrr.Base0::display(1);//通过类作用域调用基类的display(int n)函数
return 0;
}