目录
????????????????2.3.1虚函数重写的两个例外:
????????????????????????????????????????1.析构函数的重写
????????????????????????????????????????2.协变
????????2.4 C++11 override 和 final
??????????????????????? ? 2.5重载,覆盖(重写),隐藏(重定义)的对比
2.1多态的构成条件
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
注意:这里使用的虚函数与继承中的virtual修饰虚继承没有任何关系,只是共用了一个关键字而言
class Person
{
public:
virtual void BuyTicket() { cout << "买票 - 全价" << endl; }
};
class Student : public Person
{
public:
virtual void BuyTicket() { cout << "买票 - 半价" << endl; }
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(ps);
Func(st);
return 0;
}
调用的是基类的虚函数还是子类的虚函数?
????????举例1:不构成多态,未满足重写条件,调用的类型是什么就调用对应的函数。这里虽然不满足重写,但是满足隐藏(继承的知识点),但是这里不会有隐藏关系的体现,因为子类调用才会用隐藏
举例2:构成多态,指向对象的类型是什么就调用对应的虚函数
另外,有人说c++难学,有一点的原因,比如说下面的语法坑!
1.子类继承基类,可以不写 virtual 关键字 ,同样满足多态 (不推荐这样书写,很不规范)
? ? ? ? ?可以这样认为,父类不写 virtual,肯定就不是虚函数,那么子类继承肯定也不是虚函数;
? ? ? ? ?但是在这个地方,我们认为子类重写了这个虚函数,重写体现的是接口继承,重写继承父类这个函数的实现(继承后基类的虚函数被继承下来了,在派生类依旧保持虚函数属性),所以就算是子类不加 virtual 关键字,也认为是虚函数,因为父类就是虚函数,子类继承也是虚函数,当然,也可以认为是个例外。
1.析构函数的重写(基类与派生类析构函数的名字不同)
有什么作用呢?可以看看接下来的例子,
? ? ? ? 示例:
? ? ? ? 这里并不符合我们的预期,我们主张谁申请空间资源,就由谁来释放,但是这里却不一样,并且很明显造成了内存泄露。为什么会出现这个结果呢?
? ? ? ? 因为父类析构函数不加 virtual 的情况下,子类析构函数和父类析构函数构成隐藏关系。为什么构成隐藏,就是因为编译器会对析构函数名进行特殊处理,处理成 destrutor();
????????而为什么调用的是父类的析构而不是子类的析构,因为这里不满足多态,所以会根据调用者的类型去调用对应的成员函数。并且这里是子类通过切片赋值给了父类。建议看一下c++三大特性之一 继承
这里可能也会有人有疑问,为什么这里子类的析构,还会调用基类?
????????因为继承规定,派生类对象析构清理先调用派生类析构再调基类的析构,举例来说:假如父类中有指针指向一块空间,先析构父类,这时候如果子类去访问就会出现野指针的行为,但是先析构子类就不会出现这样的情况。因为c++规定:父类不能访问子类 (这个也可以简单理解一下,就是包含与被包含的关系,子类一定包括父类,但是父类不一定包括子类,也可以把父类理解成是子类的一个特殊成员)
例2:满足父子关系的指针或者引用(程序正常运行)
?final其实没多大意义,因为虚函数就是为重写而生的,重写就是为了多态而生的,所以虚函数不重写根本没多大意义。
抽象类的意义:
示例:
补充知识点:
//请问以下程序运行结果是什么?
//A: A->0 B : B->1 C : A->1 D : B->0 E : 编译出错 F : 以上都不正确
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
B* p = new B;
p->test();
return 0;
}
????????解析:首先要看这里是否构成多态。不满足多态---看调用者的类型,调用这个类型的成员函数? ? ? ?满足多态---看指向对象的类型,调用这个类型的成员函数
?构成多态还有两个条件:
1.虚函数的重写---三同(函数名,参数,返回值)? ?
2.必须是基类指针或者引用调用虚函数
?
答案是:? B? ??
因为这里首先是构成多态的,为什么不是 D?因为重写是接口继承,重写继承父类这个函数的实现,子类不仅继承了函数的实现,还继承了缺省值
变形1:
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
};
class B : public A
{
public:
virtual void func(int val = 0) { std::cout << "B->" << val << std::endl; }
virtual void test() { func(); }
};
int main(int argc, char* argv[])
{
B* p = new B;
p->test();
return 0;
}
//A: A->0 B : B->1 C : A->1 D : B->0 E : 编译出错 F : 以上都不正确
变形2:
class A
{
public:
virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
virtual void test() { func(); }
};
class B : public A
{
public:
virtual void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};
int main(int argc, char* argv[])
{
A* p = new B;
p->test();
return 0;
}
//A: A->0 B : B->1 C : A->1 D : B->0 E : 编译出错 F : 以上都不正确
?蛮有意思,可以玩一下,看看结果。