一、动态类型和静态类型
静态类型:它是变量声明时的类型或表达式生成的类型,编译时总是已知。
动态类型:它是变量或表达式表示的内存的对象的类型,运行时才已知。
考虑下面两个空类Base和Derived,B2指向对象d1,B2的静态类型是Base B2,而其动态类型是Derived d1.因为B2所指向的内存对象是d1,其类型是Derived 对象。(父类对象引用子类对象同理)
Class Base{}; Class Derived:public Base{}; Base B1; Derived d1; Base *B2 = &d1; Derived *d1 = &d1;
二、动态绑定和静态绑定
动态绑定发生的两个条件:指针或引用调用函数 和 调用的是虚函数
#include<iostream>
using namespace std;
class Base {
public:
virtual void Test() {
cout << "Base" << endl;
}
void Test2() {
cout << "Base's Test2" << endl;
}
};
class Derived :public Base {
public:
void Test() {
cout << "Derived" << endl;
}
void Test2() {
cout << "Derived's Test2" << endl;
}
};
int main(void) {
Derived d1;
Base *B2 = &d1;
Base B3 = d1;
Base *B4 = &d1;
B2->Test();
B3.Test();
B4->Test2();
system("pause");
return 0;
}

可以看到当通过父类指针调用虚函数,调用的是Derived中的Test,而B3不是通过指针调用,B4调用的不是虚函数。两个都调用的是父类中的函数,说明并没有动态绑定,而仅仅绑定的是静态类型,即Base对象。
三、sizeof(Class)的大小
1.空类
class Base { }; int main(void) { cout << sizeof(Base) << endl; = 1 system("pause"); return 0; }

虽然Base是一个什么成员都没有的空类,但是定义一个Base对象,编译器还是为为他分配1个字节的内存,因为有对象就必须在内存中占用空间。
2 .带虚函数的空类
class Base { virtual void Test() { } }; int main(void) { cout << sizeof(Base) << endl; = 4 system("pause"); return 0; }

从上面结果可以看出,有虚函数的类在对象内存模型会有个虚函数指针,指针占用字节为4.所以一个带虚函数的空类大小为4.
3.带纯纯虚函数空类大小
class Base {
virtual void Test() = 0;
};
int main(void) {
cout << sizeof(Base) << endl;
system("pause");
return 0;
}

上面结果表明带纯虚函数的空类(不带任何数据成员),还是会为其指定一个虚函数指针,尽管不能生成该类的对象。
4.多继承空类的虚函数指针
class Base1 {
virtual void Test() = 0;
};
class Base2 {
virtual void Test2() = 0;
};
class Derived :public Base1, public Base2{
virtual void Test3() {
}
};
int main(void) {
cout << sizeof(Derived) << endl;
system("pause");
return 0;
}

说明继承的父类中有虚函数,就会为其分配一个虚函数指针,那么自己的虚函数会将其放入某个虚函数指针所指向的虚函数表中。某个类的虚函数指针个数 = 其直接继承的有虚函数的父类个数。
三、虚函数中的默认实参
敲黑板:虚函数中的默认实参是静态绑定。看如下代码:
class Base {
public:
virtual void Test(int a = 3) {
cout << "Base's Test()" << endl;
cout << a << endl;
}
};
class Derived :public Base {
public:
void Test(int a = 5) {
cout << "Derived's Test()" << endl;
cout << a << endl;
}
};
int main(void) {
Derived d;
Base *B = &d;
B->Test();
system("pause");
return 0;
}

可以看到调用的是Derived中的Test函数,但是默认实参是父类Test函数中的默认参数3.
四、final 和 override
1.override是针对子类对父类虚函数进行覆盖。首先必须是父类中存在的虚函数,且返回值,函数参数必须一致。
class Base {
public:
virtual void Test(int a = 3) {
cout << "Base's Test()" << endl;
cout << a << endl;
}
void Test2() {
}
};
class Derived :public Base {
public:
void Test(int c) override;
void Test2() override;
};
void Derived::Test(char c) {
}
void Derived::Test2() {
}
int main(void) {
system("pause");
return 0;
}
出错信息如下图所示:

首先子类Test函数形参没有与父类相同,所以不是override。第二,Test2函数不是虚函数,所以也不能override。那么子类定义了两个与父类相同的函数名,则其保留了子类的两个函数,说明子类中有四个函数。其中两个继承而来。那么两两同名可以理解为在子类的函数空间重载。
当把代码改成如下所示:
class Base {
public:
virtual void Test(int a = 3) {
cout << "Base's Test()" << endl;
cout << a << endl;
}
virtual void Test2() {
}
};
class Derived :public Base {
public:
void Test(int c) override{}
void Test2() override;
};
void Derived::Test(int c) {
}
void Derived::Test2() {
}
int main(void) {
system("pause");
return 0;
}
此时子类中Test函数和Test2函数都重写了父类的虚函数。其中override只能出现在类中,不能出现在类外。
2.final
父类虚汗函数被该final修饰,则说明子类不能覆盖该虚函数函数。但是可以重定义(即函数参数不一样)代码所示:
class Base {
public:
virtual void Test(int a = 3) final{
cout << "Base's Test()" << endl;
cout << a << endl;
}
void Test2() final{
}
};
class Derived :public Base {
public:
void Test(int c) {}
void Test2();
};
void Derived::Test2(){
}
int main(void) {
system("pause");
return 0;
}

Test被final修饰,则子类不能重写。第二个Test2不是虚函数,不能被final修饰。
class Base {
public:
virtual void Test(int a = 3) final{
cout << "Base's Test()" << endl;
cout << a << endl;
}
void Test2(){
}
};
class Derived :public Base {
public:
void Test(char c) {}
void Test2();
};
void Derived::Test2(){
}
int main(void) {
system("pause");
return 0;
}
被final修饰的父类虚函数,不能被子类重写,但是可以重定义,即函数参数与父类中的虚函数不一致。要重写虚函数,必须函数参数一致且返回一致,如果参数相同但返回值不同,编译器报错,但是参数不同,则就不是重写,而是重定义,则返回值相同与不同就没有任何关系。
final 和 override都是针对虚函数,且只能出现在形参列表(包括const 或 引用修饰符)之后,只能出现在类中。
浙公网安备 33010602011771号