网站建设费用如何做账务处理,开发公司员工内部销售激励方案,做淘宝店铺装修的公司网站,做的网站百度搜索不出来1.多态的概念 通俗来说#xff0c;就是多种形态#xff0c;具体点就是去完成某个行为#xff0c;当不同的对象去完成时会产生出不同 的状态。 2.多态的定义及实现
2.1多态的构成条件
多态是在不同继承关系的类对象#xff0c;去调用同一函数#xff0c;产生了不同的行为…1.多态的概念 通俗来说就是多种形态具体点就是去完成某个行为当不同的对象去完成时会产生出不同 的状态。 2.多态的定义及实现
2.1多态的构成条件
多态是在不同继承关系的类对象去调用同一函数产生了不同的行为。比如Student继承了Person。 Person对象买票全价Student对象买票半价。
在继承中要构成多态需要满足两个条件 必须通过基类的指针或者引用调用虚函数。被调用的函数必须是虚函数且派生类必须对基类的虚函数进行重写 例子
2.2 虚函数
虚函数即被virtual修饰的类成员函数称为虚函数。 class Person {public:virtual void BuyTicket() { cout 买票-全价 endl;}}; 2.3虚函数的重写 虚函数的重写(覆盖)派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类 型、函数名字、参数列表完全相同)称子类的虚函数重写了基类的虚函数。 class Person
{public:virtual void BuyTicket() { cout 买票-全价 endl; }
};class Student : public Person
{public:
//子类对基类虚函数进行了重写virtual void BuyTicket() { cout 买票-半价 endl;
}
注 在重写基类虚函数时派生类的虚函数在不加virtual关键字也可以但是该种写法不是很规范。
2.4虚函数重写的两个例外
2.4.1协变(基类与派生类虚函数返回值类型不同 派生类重写基类虚函数时与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引 用派生类虚函数返回派生类对象的指针或者引用时称为协变。 class A{}
class B : public A {};class Person {public:
//返回值是基类A*virtual A* f() {return new A;}};class Student : public Person {public:
//返回值是派生类B* 此时这个虚函数尽管返回值不同仍然完成虚函数重写virtual B* f() {return new B;}};
2.4.2析构函数的重写(基类与派生类析构函数的名字不同)
如果基类的析构函数为虚函数此时派生类析构函数只要定义就构成析构函数的重写。虽然函数名不相同看起来违背了重写的规 则但最后编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处 理成destructor。
class Person{
public:virtual ~Person() {cout ~Person() endl;}};
class Student : public Person {public:virtual ~Student() { cout ~Student() endl; }};
int main(){Person* p1 new Person;Person* p2 new Student;delete p1;//因为完成了虚函数的重新所以p1指向的对象是Person,调用~Rerson();delete p2;//虽然P2是Person*,但多态与指定的对象有关所以调用~Student();return 0;}
注 多态与指向的对象有关指向那个对象就调用它的虚函数。不满足多态与 调用的对象的类型有关类型是什么就调用它的虚函数。
2.5 C11 override 和 final C对函数重写的要求比较严格但是有些情况下由于疏忽可能会导致函数名字母次序写反而无法构成重载而这种错误在编译期间是不会报出的只有在程序运行时没有得到预期结果才来 debug因此C11提供了override和final两个关键字可以帮助用户检测是否重写。 2.5.1. final修饰虚函数表示该虚函数不能再被重写
{public:virtual void Drive() final {}};class Benz :public Car{public:virtual void Drive() {cout Benz-舒适 endl;}};
2.5.2 override: 检查派生类虚函数是否重写了基类某个虚函数如果没有重写编译报错
class car{
public:virtual void Drive(){}};class Benz :public Car {public:virtual void Drive() override {cout Benz-舒适 endl;}}
2.6 函数重载、覆盖(重写)、隐藏(重定义)的对比 3.抽象类
3.1概念 在虚函数的后面写上 0 则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口类抽象类 不能实例化出对象。派生类继承后也不能实例化出对象只有重写纯虚函数派生类才能实例化出对象。纯虚函数规范了派生类必须重写另外纯虚函数更体现出了接口继承 class Car{public:virtual void Drive() 0;};class Benz :public Car{public:virtual void Drive(){cout Benz-舒适 endl;}
3.2 接口继承和实现继承 普通函数的继承是一种实现继承虚函数的继承是一种接口继承派生类继承的是基类虚函数的接口目的是为了重写达成多态继承的是接口. 4.多态的原理
4.1虚函数表
// sizeof(Base)是多少
class Base{public:virtual void Func1(){cout Func1() endl;}private:int _b 1;} 注在这里class Base 并不是只有一个整形int 4个字节因为 void Func1()前面加了 virtual 是虚函数则除了_b成员还多一个__vfptr虚函数指针放在对象的前面这样sizeof(Base)为8个字节。
对象中的这个指针我们叫做虚函数表指针(v代表virtualf代表 function)。一个含有虚函数的类中都至少都有一个虚函数表指针因为虚函数的地址要被放到虚函数表中 虚函数表也简称虚表 虚函数表本质是一个存虚函数指针的指针数组一般情况这个数组最后面放了一个nullptr。 4.2虚函数的共用 注Base这个类有两个虚函数所以虚函数表存放了两个指针并且Fun1()函数指针为0x0b31087 4.3虚函数存在哪的虚表存在哪 虚函数会被编译成指令放在代码段 而上面我们也提到过可以理解虚函数共用一个虚函数表基类b对象和派生类d对象虚表是不一样的即放在公共部分所以在代码段。
4.4多态的原理 5.动态绑定与静态绑定 静态绑定又称为前期绑定(早绑定)在程序编译期间确定了程序的行为也称为静态多态比如函数重载动态绑定后期绑定(晚绑定)是在程序运行期间根据具体拿到的类型确定程序的具体行为调用具体的函数也称为动态多态。 6.例子 以下程序输出结果是什么
class A{public:virtual void func(int val 1){ std::coutA- val std::endl;}virtual void test(){ func();}};class B : public A{public:void func(int val0){ std::coutB- val std::endl; }};int main(int argc ,char* argv[]){B*p new B;p-test();return 0;
}
前面我们也提过虚函数的继承是一个接口继承 所以实际上继承的是 void func(int val1)
函数体则为{ std::coutB- val std::endl; }。所以程序运行结果应该为B—1.