网站如何做死链接提交,自己做轴承网站,自己做图片网站,企业培训机构网站源码#x1f308;个人主页#xff1a;羽晨同学
#x1f4ab;个人格言:“成为自己未来的主人~” 多态 多态就是不同类型的对象#xff0c;去做同一个行为#xff0c;但是产生的结果是不同的。 比如说#xff1a; 都是动物叫声#xff0c;猫是喵喵#xff0c;狗是汪汪个人主页羽晨同学
个人格言:“成为自己未来的主人~” 多态 多态就是不同类型的对象去做同一个行为但是产生的结果是不同的。 比如说 都是动物叫声猫是喵喵狗是汪汪它们的叫声是不相同的。 多态的定义和实现
多态的构成条件
多态是在不同继承关系的类对象去调用同一函数产生了不同的行为。
在继承中构成多态需要两个条件
必须通过基类的指针或者引用调用虚函数。被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写。
class Person
{
public:virtual void BuyTicket(){cout 买票-全价 endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout 买票-半价 endl;}
};
class Soldier:public Person
{
public://重写/覆盖virtual void BuyTicket(){cout 买票-优先 endl;}
};
//多态条件
//1.虚函数重写
//2.父类指针或者引用调用虚函数
void func(Person p)
{p.BuyTicket();
}
int main()
{Person p;Student st;Soldier so;func(st);func(so);return 0;
}你看这样子就实现了多态。
虚函数
被virtual修饰的函数就叫做虚函数
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout 买票-半价 endl;}
};
虚函数的重写
虚函数的重写覆盖派生类和基类中的某个函数函数名相同参数相同返回值相同就称子类的虚函数重写了基类的虚函数。
class Person
{
public:virtual void BuyTicket(){cout 买票-全价 endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout 买票-半价 endl;}
};
class Soldier:public Person
{
public://重写/覆盖virtual void BuyTicket(){cout 买票-优先 endl;}
};
你看这样其实就是标准的构成了函数重写的代码。
但是由于继承的存在所以其实派生类不写virtual也会构成函数重写。
class Person
{
public:virtual void BuyTicket(){cout 买票-全价 endl;}
};
class Student:public Person
{//重写/覆盖virtual void BuyTicket(){cout 买票-半价 endl;}
};
class Soldier:public Person
{
public://重写/覆盖void BuyTicket(){cout 买票-优先 endl;}
};
比如上面的这样但是这样子是不规范的所以我们最好加上virtual。
虚函数重写的两个例外
协变基类和派生类的返回值类型不同
派生类重写基类虚函数时与基类虚函数返回值类型不同即基类虚函数返回基类对象的指针或者引用派生类对象返回派生类对象的指针或者引用这个就叫做协变。
class A{};
class B :public A {};class Person
{
public:virtual A* BuyTicket(){cout 买票-全价 endl;return 0;}
};
class Student:public Person
{//重写/覆盖virtual B* BuyTicket(){cout 买票-半价 endl;return 0;}
};
class Soldier:public Person
{
public://重写/覆盖virtual B* BuyTicket(){cout 买票-优先 endl;return 0;}
};
//多态条件
//1.虚函数重写
//2.父类指针或者引用调用虚函数
void func(Person p)
{p.BuyTicket();
}
int main()
{Person p;Student st;Soldier so;func(st);func(so);return 0;
}这样子我们就实现了协变。
析构函数的重写
我们先来看一下下面的这段代码
class Person
{
public:virtual ~Person(){cout virtual ~Person() endl;}
};
class Student:public Person
{
public:protected:int* _ptr new int[10];
};
int main()
{Person* p1 new Student;delete p1;return 0;
}
在这段代码当中我们删除了p1但是结果调用的是基类的析构函数。 那为什么会这样呢是因为编译器对析构函数的名字做了特殊的处理编译后的析构的名字同一处理为了destructor这样子的话基类和父类的析构函数就构成了隐藏。
这样子调用的话那么会造成内存泄漏。
但是只要我们构成了多态那么就解决了这个问题。
class Person
{
public:virtual ~Person(){cout virtual ~Person() endl;}
};
class Student:public Person
{
public:virtual ~Student(){cout virtual ~Student() endl;}
protected:int* _ptr new int[10];
};
int main()
{//Student st;Person* p1 new Student;delete p1;Person* p2 new Person;delete p2;return 0;
}
override 和final
上面可以看出C对函数重写的要求比较严格但是有些情况下由于疏忽可能会导致函数字母次序写反而无法构成重写这种错误在编译期间是无法报出的只有在程序运行时没有到预期结果才会debug这样子得不偿失。
final修饰虚函数表示该虚函数不能再被重写
class A final
{
public:static A CreatObj(){return A();}
private:A(){};
};
class V:public A
{};
int main()
{A::CreatObj();return 0;
}所以在这个代码中会出现报错的问题。
override检查派生类虚函数是否重写了基类某个虚函数如果没有重写编译报错
class Car {
public:virtual void Drive() {}
};
class Benz :public Car {
public:virtual void Drive() override { cout Benz-舒适 endl; }
};
int main()
{Car a;return 0;
}重载覆盖重写隐藏重定义的对比
重载
两个函数在同一个作用域。
函数名相同参数不同。
重写覆盖
两个函数分别在基类和派生类的作用域。
函数名参数返回值都必须相同协变除外
两个函数必须是虚函数
重定义
两个函数分别在基类和派生类的作用域
函数名相同
来年各个基类和派生类的同名函数不构成重写就是重定义
抽象类
在虚函数后面写上0则这个函数为纯虚函数包含纯虚函数的类叫做抽象类也叫接口类抽象类不能实例化出对象。派生类继承后也不能实例化出对象只有重写纯虚函数派生类才能实例化出对象纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承。
class Car
{
public:virtual void Drive() 0;
};
class Benz :public Car
{
public:virtual void Drive(){cout BWM-舒适 endl;}
};
class BWM :public Car
{
public:virtual void Drive(){cout BWM-操控 endl;}
};
int main()
{Car a;Benz bz;return 0;
}
在这个代码中因为基类中有纯虚函数所以不能实例化出对象而派生类中对纯虚函数进行了重写所以可以进行实例化。
多态的原理
虚函数表
class Base
{
public:virtual void Func1(){cout Func1() endl;}
private:int _b 1;
};
int main()
{coutsizeof(Base);return 0;
}
大家可以想一想这个结果是什么。
答案是8那么这是为什么呢
我们来仔细看一下Base里面有什么。 我们可以看到的是在这里面还存在了一个虚函数表的指针这个就是存放虚函数的地址的地方。 class Base
{
public:virtual void func1(){cout Base::func1() endl;}virtual void func2(){cout Base::func2() endl;}void func3(){cout Base::func3() endl;}
private:int _b 1;
};
class Derive : public Base
{
public:virtual void func1(){cout Derive::Func1() endl;}
private:int _d 2;
};
void func1(Base* p)
{p-func1();p-func3();
}
int main()
{Base b;Derive d;func1(b);func1(d);return 0;
} 通过这个结果我们可以得到的是派生类中也有一个虚表的指针里面存放的有两部分一个是自己的成员一个是从基类继承下来的成员。基类b对象和派生类d对象虚表是不一样的我们这里发现func1完成了重写所以d中存放的是Derieve::Func1();所以虚函数的重写也叫覆盖。不是虚函数函数的指针不会被放进虚表虚表存放在对象里面虚表里面存放的是虚函数的指针虚函数存放在代码段那里。 满足多态以后的函数调用不是在编译时确定的是运行起来以后到对象中的找的不满足多态的函数调用是编译时就确认好的。
动态绑定和静态绑定 在程序编译期间确定了程序的行为叫做静态绑定。 在程序运行期间在对象中找的行为叫做动态绑定 好了本次的文章就到这里了我们下次再见。