原反补移码

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

一、动态类型和静态类型

静态类型:它是变量声明时的类型或表达式生成的类型,编译时总是已知。

动态类型:它是变量或表达式表示的内存的对象的类型,运行时才已知。

考虑下面两个空类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 或 引用修饰符)之后,只能出现在类中。

posted on 2020-04-29 11:02  原反补移码  阅读(42)  评论(0)    收藏  举报