自己做的网站怎么放图片,酒仙网技术开发与网站建设方面,赣州经开区最新规划图,wordpress qq 群1.final
final在继承和多态中都可以使用#xff0c;在继承中是指不想将自己被继承#xff0c;在多态中是指不想该函数被重写#xff0c;比较简单#xff0c;下面是一些使用例子。 2.纯虚函数
当我们需要抽象一个类的时候#xff0c;我们就需要用到纯虚函数。所谓抽象的类…1.final
final在继承和多态中都可以使用在继承中是指不想将自己被继承在多态中是指不想该函数被重写比较简单下面是一些使用例子。 2.纯虚函数
当我们需要抽象一个类的时候我们就需要用到纯虚函数。所谓抽象的类是指高度概括的需要针对不同事物有不同处理的。如植物是一种抽象的类而像苹果、香蕉就是具象的单独讨论植物太过庞大没有太大意义因此我们的重心放在由植物具象出来的苹果我们可以具体讨论它的成分、营养价值等。理解了这个例子就能理解为什么有抽象类纯虚函数的存在了。 这就是一个纯虚函数就是在虚函数后面加上 0它对应的类就叫抽象类。注意只要有一个纯虚函数这个类就叫抽象类抽象类不能被实例化就算你不打算用这个纯虚函数。唯一能做的就是调用这个类里面的static成员因为它们不需要实例化就能调用 这么做的意义就在于纯虚函数对应的类本身就高度抽象实例化它没有意义。但我们可以讨论将它具象化的事物这就要用到虚函数的重写功能。我们可以理解纯虚函数存在的意义是依赖于虚函数的性质存在的这里需要我们深刻思考。 3.继承、多态难点
继承、多态的用法、意义几乎讲的差不多了绝大多数情况下已经够用了只不过在极少数情况下仍有一些坑。
1多态调用重写的函数
先看下面的代码想想结果是什么 #include iostream
using namespace std;class A
{
public:virtual void test(int a 0){}
};class B final : public A
{
public:void test(int a){cout a endl;}
};int main()
{A* a new B;a-test();return 0;
}
不少人会想这难道不报错吗但结果是 我们需要知道当构成多态和重写时调用函数是以父类声明子类定义进行的对于三个类及以上都是如此这个父类指的是构成多态的父类 我们也可以进一步理解为什么只需要父类写virtual子类可以不写因为子类的函数声明根本没有意义在多态中写不写都是以父类的声明为标准。但是在多态语法以外就不会出现这种反直觉处理情况了。
2继承调用父类函数时this的类型变化
先看看下面的代码想想test2的隐含的this指针是B*还是A* #include iostream
using namespace std;class A
{
public:virtual void test(int a 0){} void test2(){test();}
};class B final : public A
{
public:void test(int a 1){cout a endl;}
};int main()
{B* a new B;a-test2();return 0;
}既然是B*调用函数那理所应当应该是B*为形参来接受啊但实际不是这么理解的。
当子类去调用父类的成员函数时隐含的指针类型始终是父类的。要理解这里我们假设这个指针的类型是子类的那如果子类又写了一个一模一样的函数构成隐藏那么就会因为参数和假设的函数完全相同而报错所以是行不通的。
当子类调用父类时this指针会发生一次赋值兼容转换这里是从B*赋值兼容转换为A*赋值兼容转换为指针只会影响访问的方式指针的值指向的内容都不会改变。但学了多态之后我们是否可以将这种特性和多态的形成条件结合起来呢上面这段代码就是如此。 结合上一个易错点这段代码的最终结果是 3多态访问限制的特殊处理
先看看下面的代码看看是否能够正常访问 #include iostream
using namespace std;class A
{
public:virtual void Test(){cout A endl;}
};class B : public A
{
private:void Test(){cout B endl;}
};int main()
{A* p new B;p-Test();return 0;
}很多人以为p的类型是A*A访问不了B但其实程序运行没有问题 我们要理解访问限定符限制的是什么是防止其它类调用private的函数这里p是一个指针本身就指向B对象的空间只不过访问方式按A进行。由于符合多态的条件就按虚函数表进行访问。那么问题在于B会不会阻止呢
我们先看看什么情况是会阻止的 我们发现无论在A还是在main函数中都没有办法调用B中的private成员这也符合我们之前的预期。但是为什么A* p new B; p-Test();这种操作就可以呢
事实上这是多态中的特殊处理当我们用父类的指针或引用来访问子类的虚函数时是会以父类的访问限定符为标准的。子类的限制不会起到作用。同理就算子时public父是private那么就无法访问。如何理解
当以父类的指针和引用调用虚函数时是按照虚函数表的地址直接去调用函数根本不会经过类调用函数只会受到调用方父类的访问限定符的限制。 一般建议都设为public
4.动静态绑定
动静态绑定都是为了定位一个函数从反汇编的角度上讲就是确定call的对应的地址是什么只不过两者的方式有一定的区别。
1动态绑定多态调用函数的核心时动态绑定也叫运行时绑定。也就是借助虚函数表在这个函数指针数组中确定函数的地址。2静态绑定我们平时写的函数都可以认为是静态绑定包括函数重载、普通函数、模板函数函数如果声明定义在一起就在编译后进符号表如果声明定义分离在两个文件则在链接时进符号表运行时是根据符号表来查找函数。
在多态中在满足动态绑定的情况下我们指定类域调用函数那就自动转为静态绑定就失去了多态的特性。 #include iostream
using namespace std;class A
{
public:virtual void Test(){cout A endl;}
};class B final : public A
{
public:void Test(){cout B endl;}
};int main()
{A* p new B;p-Test();p-A::Test();return 0;
}运行结果 5.继承、多态的一些知识点和处理技巧
1多用const修饰函数保证匿名对象传参可以调用函数
2函数第一句的指令理解为函数的地址成员函数要打印它们的地址函数名前要加其余函数函数名就是它的地址可加可不加但成员函数一定要加
3cout打印地址很麻烦char*不会打印地址会按字符串去打印这跟流插入的重载有关。有几个关于函数指针的重载会导致出现bug打印地址很受阻最好使用printf
4关联性强的类型之间支持隐式类型转换如整型家族double内置类型、指针之间有的支持强转如int和int*。
关联性弱的自定义类型想取头地址可以使用*((int*)Base)虚函数表的地址就在类的开头
5只有virtual修饰的成员函数才能叫作虚函数而像static修饰的成员函数、全局函数都不能定义为虚函数全局定义的虚函数没有意义static修饰的成员函数不属于对象就算加了virtual也进不了虚函数表没有意义。
6virtual修饰的成员函数声明定义分离时定义处不写virtual
7友元不是成员函数所以不能用virtual修饰
8多继承可能有多张虚函数表按继承顺序排序但单继承对应的就只有一张虚函数表如果多继承后自己又写了虚函数则默认放在第一张虚函数表后面
9如果不重写虚函数那共用同一个函数如果所有的函数都不重写两个类存的函数的地址都相同但是这对应两张虚函数表开辟的是不同的空间
10虚函数表是在编译期间就形成了。而多态是动态绑定运行时绑定/晚期绑定是因为编译时编译器只负责检查语法错误而不负责读取内容只有运行起来才知道函数调用的地址。