【C/C++】覆盖

本文通过两个问题探讨了C++中构造函数的构造顺序和虚函数的覆盖行为。在问题1中,详细解释了不同成员函数调用的逻辑,指出即使通过类名限定符指定,调用的仍然是虚函数对应的实现。在问题2中,说明了指针类型转换后的虚函数调用情况,以及悬挂指针可能导致的覆盖现象。答案分别是1 1 1 1 1 0 1 1和B。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题1

以下代码的输出结果是什么?

#include <iostream>
using namespace std; 

class A 
{
	protected:
		int m_data; 
	
	public:
		A(int data = 0)
		{
			m_data = data; 
		}
		int GetData() 
		{
			return doGetData(); 
		}
		virtual int doGetData()
		{
			return m_data; 
		}
}; 

class B : public A
{
	protected:
		int m_data; 
	
	public:
		B(int data = 1)
		{
			m_data = data; 
		}
		virtual int doGetData()
		{
			return m_data; 
		}
}; 

class C	: public B
{
	protected:
		int m_data; 
	
	public:
		C(int data = 2)
		{
			m_data = data; 
		}
}; 

int main(int argc, char *argv[]) 
{
	C c(10); 
	
	cout << c.GetData() << endl; 
	cout << c.A::GetData() << endl; 
	cout << c.B::GetData() << endl; 
	cout << c.C::GetData() << endl; 
	cout << c.doGetData() << endl; 
	cout << c.A::doGetData() << endl; 
	cout << c.B::doGetData() << endl; 
	cout << c.C::doGetData() << endl; 
	
	return 0; 
}

问题2

以下代码的输出结果是什么?

#include <iostream>
using namespace std; 

class A
{
	public:
		void virtual f() 
		{
			cout << "A" << endl;
		}
}; 

class B : public A
{
	public:
		void virtual f()
		{
			cout << "B" << endl; 
		}
}; 

int main()
{
	A* pa = new A(); 
	pa -> f(); 
	
	B* pb = (B*) pa; 
	pb -> f(); 
	
	delete pa, pb; 
	
	pa = new B(); 
	pa -> f(); 
	
	pb = (B*) pa; 
	pb -> f(); 
	
	return 0; 
}

A. A A B A

B. A A B B 

C. A A A B

D. A B B A

=======================================================================================

问题1解析:

构造函数从最初始的基类开始构造,各个类的同名变量没有形成覆盖,都是单独的变量。理解这两个重要的C++特性后解决这个问题就比较轻松了。下面我们详解这几条输出语句。

cout << c.GetData() << endl; 

本来是要调用C类的GetData(),C中未定义,故调用B中的,但是B中也未定义,帮调用A中的GetData(),因为A中的doGetData()是虚函数,所以调用B类中的doGetData(),而B类的doGetData()返回B::m_data,故输出1。

cout << c.A::GetData() << endl;

因为A中的doGetData()是虚函数,又因为C类中未重定义该接口,所以调用B类中的doGetData(),而B类的doGetData()返回B::m_data,故输出1。

cout << c.B::GetData() << endl;

肯定返回1了。

cout << c.C::GetData() << endl;

因为C类中未重定义GetData(),故调用从B继承来的GetData(),但是B类也未定义,所以调用A中的GetData(),因为A中的doGetData()是虚函数,所以调用B类中的doGetData(),而B类的doGetData()返回B::m_data,故输出1。

cout << c.doGetData() << endl;

肯定是B类的返回值1了。

cout << c.A::doGetData() << endl;

因为直接调用了A的doGetData(),所以输出0。

cout << c.B::doGetData() << endl;

因为直接调用了B的doGetData(),所以输出1。

cout << c.A::doGetData() << endl;

因为C中未定义该接口,所以调用B类中的doGetData()返回B::m_data,故输出1。

这里要注意存在一个就近调用,如果父辈存在相关接口则优先调用父辈接口,如果父辈也不存在相关接口则调用祖父辈接口。

答案:1 1 1 1 1 0 1 1 

问题2解析:

这是一个虚函数覆盖虚函数的问题。A类的f函数是一个虚函数,虚函数是被子类同名函数所覆盖的。而B类里的f函数也是一个虚函数,它覆盖A类f函数的同时,也会被它的子类覆盖。但是在B* pb = (B*)pa;里面,该语句的意思是转化pa为B类型并新建一个指针pb,将pa复制到pb。但这里有一点请注意,就是pa的指针始终没有发生变化,所以pb也指向pa的f函数。这里并不存在覆盖的问题。

delete pa, pb;删除了pa和pb所指向的地址,但pa,pb指针并没有删除,也就是我们通常说的悬浮指针。现在重新给pb指向新地址,所指向的位置是B类的,而pa指针类型是A类的,所以就产生了一个覆盖。pa->f();的值是B。

pb = (B*) pa;转化B类指针给pb赋值,但pa所指向的f函数是B类的f函数,所以pb所指向的f函数是B类的f函数。pb->f();的值是B。

答案:B

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值