面向对象程序设计

1. 类型转换与继承

2. 虚函数


1. 虚函数与虚函数列表

  含有(纯)虚函数的类会有一个虚函数列表。虚函数列表是一个指针数组,里面存放的是指向虚函数的函数指针。类的每一个对象都会有一个虚函数列表指针,指向类的虚函数列表。

2. 多重继承时的虚函数列表

3. 动态绑定

class Person {
public:
	Person() { cout << "--Created person." << endl; }
	virtual void id() { cout << "This is a person." << endl; }
	virtual ~Person() { cout << "--Released person." << endl; }
};

class Man :public Person {
public:
	Man() { cout << "--Created man." << endl; }
	void id() { cout << "This is a man." << endl; }
	~Man() { cout << "--Released man." << endl; }
};

void testP(Person* p) { p->id(); } // 用基类指针调用所指对象实际类型中定义的函数。
void testR(Person& p) { p.id(); } // 用基类引用调用所引对象实际类型中定义的函数。
void testO(Person p) { p.id(); } // 不用指针和引用只能调用形参类型中定义的函数。

int main() {
	{
		Man m1 = Man();	// 基类构造函数、派生类构造函数。
		Person *p = &m1;
		p->id();		// 派生类函数。
		m1.id();		// 派生类函数。
		testP(&m1);		// 派生类函数。
		testR(m1);		// 派生类函数。
		testO(m1);		// 基类拷贝构造函数、基类函数、基类析构函数。
						// 派生类析构函数、基类析构函数。
	}
	{
		Man* m2 = new Man();// 基类构造函数、派生类构造函数。
		m2->id();			// 派生类函数。
		testP(m2);			// 派生类函数。
		testR(*m2);			// 派生类函数。
		testO(*m2);			// 基类拷贝构造函数、基类函数、基类析构函数。
		delete m2;			// 派生类析构函数、基类析构函数。
	}
	{
		Person* m3 = new Man();	// 基类构造函数、派生类构造函数。
		m3->id();				// 派生类函数。
		testP(m3);				// 派生类函数。
		testR(*m3);				// 派生类函数。
		testO(*m3);				// 基类拷贝构造函数、基类函数、基类析构函数。
		delete m3;				// 基类析构函数。/派生类析构函数、基类析构函数。
	}
	{
		Person m4 = Man();	// 基类构造函数、派生类构造函数、基类拷贝构造函数、派生类析构函数、基类析构函数。
		m4.id();			// 基类函数。
							// 基类析构函数。
	}
}

使用多态
  1. 形参为基类指针或引用:通过形参调用的是实参实际类型中定义的函数(第15、16行)。
  2. new创建的派生类对象赋值给基类指针:通过该基类指针调用的是派生类中定义的函数(第39、40行)。
  3. 基类指针指向派生类对象:通过该基类指针调用的是派生类中定义的函数(第22行)。
  上面三种情况本质是一样的:通过基类指针或引用调用的是对象实际类型中定义的函数。

注意
  多态三要素:虚函数或纯虚函数、继承、指针或引用。缺少任一个都不行。如第47行没有指针/引用,调用的是类型说明符Person中定义的函数。
  派生类中重写的虚函数可以是virtual void id() override;也可以简化为void id();

多态的实现原理

  1. 类的虚函数列表vtbl是一个虚函数指针数组,元素是指向虚函数的指针。每个对象包含一个虚函数列表指针*__vptr,指向类的虚函数列表。
  2. 对Base* b = new Derive();来说,基类指针b指向的是派生类Derive的匿名对象d的内存地址,这块内存中的虚表指针指向的是派生类Derive的虚表。于是通过基类指针b调用的是派生类Derive中的虚函数(重写的Base的虚函数)。
  3. 对Base b = Derive();来说,因为基类对象b是由基类Base的拷贝构造函数创建的,拷贝构造函数拷贝派生类Derive的匿名对象d的数据时并没有拷贝d的虚表指针。所以基类对象b的虚表指针指向的是基类Base的虚表(这符合对象的虚表指针指向类的虚表原则)。于是通过基类对象b调用的是基类Base中的虚函数。
    多态与虚函数】【多态与指针/引用

虚函数与虚析构函数
  delete指向派生类对象的基类指针时(第44行),基类的析构函数须是虚函数才调用派生类的析构函数,否则只调用基类的析构函数。delete其它类型的指针,派生类和基类的析构函数都会被调用。

3. 访问控制与继承


1. 控制对象

  访问控制是对类成员的控制,包括构造函数、析构函数、静态成员函数。友元不是成员函数,所以不受访问控制限制。

2. 访问控制与重写

  无论(纯)虚函数在基类中的访问说明符是什么,也无论继承方式是public、protected还是private,派生类中重写的函数的访问控制都以派生类中的访问说明符为准。

3. public 继承时,基类成员的可见性

  标题行(第一行)是基类成员的访问说明符。

public继承publicprotectedprivate
基类对象××
基类函数
基类的友元函数
派生类函数×

3. 继承方式对基类成员可见性的影响

  横标题行是基类成员的访问说明符,纵标题列是继承方式,内容部分是基类成员在派生类中的访问控制类型:

继承方式publicprotectedprivate
publicpublicprivateinaccessible
protectedprotectedprotectedinaccessible
privateprivateprivateinaccessible

4. 参考


  1. 访问控制与继承
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值