C++基础教程面向对象(学习笔记(63))

使用operator <<打印继承的类

考虑以下使用虚函数的程序:

class Base
{
public:
	Base() {}
 
	virtual void print() const { std::cout << "Base";  }
};
 
class Derived : public Base
{
public:
	Derived() {}
 
	virtual void print() const override { std::cout << "Derived"; }
};
 
int main()
{
	Derived d;
	Base &b = d;
	b.print(); // 将调用Derived :: print()
 
	return 0;
}

到目前为止,您应该对b.print()将调用Derived :: print()这一事实感到满意(因为b指向Derived类对象,Base :: print()是一个虚函数,并且Derived :: print()是一个覆盖)。

虽然调用这样的成员函数来输出是可以的,但这种函数风格与std :: cout不能很好地混合:

#include <iostream>
int main()
{
	Derived d;
	Base &b = d;
 
        std::cout << "b is a ";
	b.print(); // 麻烦,我们必须打破我们的print语句来调用这个函数            
	std::cout << '\n';
 
	return 0;
}

在本课中,我们将介绍如何使用继承覆盖operator << ,以便我们可以按预期使用operator <<,如下所示:

std::cout << "b is a " << b << '\n'; // 更好一点

operator<<的挑战

让我们从典型的方式重载operator <<开始:

#include <iostream>
class Base
{
public:
	Base() {}
 
	virtual void print() const { std::cout << "Base";  }
 
	friend std::ostream& operator<<(std::ostream &out, const Base &b)
        {
            out << "Base";
            return out;
        }
};
 
class Derived : public Base
{
public:
	Derived() {}
 
	virtual void print() const override { std::cout << "Derived"; }
 
	friend std::ostream& operator<<(std::ostream &out, const Derived &d)
        {
            out << "Derived";
            return out;
        }
 
};
 
int main()
{
    Base b;
    std::cout << b << '\n';
 
    Derived d;
    std::cout << d << '\n';
 
    return 0;
}

因为这里不需要虚函数解析,所以这个程序按照我们的预期工作,并打印:

Base
Derived
现在,请考虑以下main()函数:

int main()
{
    Derived d;
    Base &bref = d;
    std::cout << bref << '\n';
    
    return 0;
}

该程序打印:

Base
那可能不是我们所期待的。发生这种情况是因为我们处理Base对象的operator <<版本不是虚拟的,所以std :: cout << bref调用处理Base对象而不是Derived对象的operator <<版本。

这就是挑战。

我们可以让operator<<虚拟?

如果这个问题是operator<<不是虚拟的,我们不能简单地将它变为虚拟吗?

最简洁的答案是不。有许多的原因。

首先,只有成员函数才能被虚拟化 - 这是有道理的,因为只有类可以从其他类继承,并且没有办法覆盖一个存在于类之外的函数(你可以重载非成员函数,但不能覆盖它们) )。因为我们通常将operator <<实现为友元函数,而友元函数不被视为成员函数,所以operator <<的友元函数版本不具有虚拟化的资格。(有关我们为何以这种方式实现运算符的回顾,请重新阅读前面使用成员函数重载运算符)。

其次,即使我们可以虚拟化operator<<存在Base :: operator <<和Derived :: operator <<的函数参数不同的问题(Base版本将采用Base参数,Derived版本将采用Derived参数)。因此,Derived版本不会被视为Base版本的覆盖,因此不适合虚拟函数解析。

那么程序员要做什么?

解决方案

事实证明,答案非常简单。

首先,我们像往常一样在我们的基类中设置operator <<作为友元函数。但是,我们不是让operator<<自己进行打印,而是将该责任委托给可以虚拟化的普通成员函数!

这是完整的解决方案:

#include <iostream>
class Base
{
public:
	Base() {}
 
	// 在这里是我们重载的operator<<
	friend std::ostream& operator<<(std::ostream &out, const Base &b)
	{
		// 将打印的责任委派给成员函数print()
		return b.print(out);
	}
 
	// 我们将依靠成员函数print()来进行实际打印
	//由于print是普通的成员函数,因此可以进行虚拟化
	virtual std::ostream& print(std::ostream& out) const
	{
		out << "Base";
		return out;
	}
};
 
class Derived : public Base
{
public:
	Derived() {}
 
	// 这是我们的覆盖打印功能来处理Derived案例
	virtual std::ostream& print(std::ostream& out) const override
	{
		out << "Derived";
		return out;
	}
};
 
int main()
{
	Base b;
	std::cout << b << '\n';
 
	Derived d;
	std::cout << d << '\n'; // 请注意,即使没有显式处理派生对象的operator<< 
 
	Base &bref = d;
	std::cout << bref << '\n';
 
	return 0;
}

以上程序适用于所有三种情况:

Base
Derived
Derived

让我们更详细地研究一下。

首先,在Base的情况下,我们调用operator <<,它调用虚函数print()。由于我们的Base引用参数指向Base对象,因此b.print()解析为Base :: print(),它执行打印。这里没什么特别的。

在Derived案例中,编译器首先查看是否存在带有Derived对象的operator <<。没有一个,因为我们没有定义一个。接下来,编译器会查看是否有一个带有Base对象的operator <<。有,所以编译器将我们的Derived对象隐式向上转换为Base&并调用该函数(我们可以自己完成这个upcast,但编译器在这方面很有帮助)。然后该函数调用virtual print(),它解析为Derived :: print()。

请注意,我们不需要为每个派生类定义operator<!处理Base对象的版本适用于Base对象和从Base派生的任何类!

第三种情况是前两种情况的混合。首先,编译器将变量bref与带有Base的operator <<匹配。这会调用我们的虚拟print()函数。由于Base引用实际上指向Derived对象,因此我们按照预期解析为Derived :: print()。

问题解决了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值