(从原始帖子编辑,将“BaseMessage”更改为“const BaseMessage&”)
大家好, 我是C ++的新手,所以我希望大家能帮助我“看看我的方式的错误”。
我有一个消息层次结构,我正在尝试使用抽象基类来强制执行 一个界面。特别是,我想强制每个派生的消息提供一个重载 <<操作
当我尝试这样做时:
class BaseMessage
{
public:
// some non-pure virtual function declarations
// some pure virtual function declarations
virtual ostream& operator<<(ostream& stream, const BaseMessage& objectArg) = 0;
}
编译器抱怨
“错误:无法将参数'objectArg'声明为抽象类型'BaseMessage'
我相信这里也涉及“朋友”问题,但当我试图将其声明为:
virtual friend ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;
编译器添加了一个附加错误
“错误:虚拟功能不能成为朋友”
有没有办法确保我的所有派生(消息)类都提供“&lt;&lt;” ostream运算符?
非常感谢,
史蒂夫
答案 0 :(得分:41)
这个常见的约定是在基础级别有一个friend
输出操作符并让它调用私有虚函数:
class Base
{
public:
/// don't forget this
virtual ~Base();
/// std stream interface
friend std::ostream& operator<<( std::ostream& out, const Base& b )
{
b.Print( out );
return out;
}
private:
/// derivation interface
virtual void Print( std::ostream& ) const =0;
};
答案 1 :(得分:3)
抽象类无法实例化,所以这样做:
virtual ostream& operator<<(ostream& stream, const Base &objectArg) = 0;
虚函数必须是实例成员函数,而友元函数是非成员函数,因此不能声明为虚函数。
类似地,静态函数不能是虚拟的,因为它的类方法不是实例方法。
我的建议是:
class Base {
public:
virtual ostream& print (ostream& stream) const = 0;
};
class Derived :public Base {
public:
virtual ostream& print (ostream& stream) const { //do something }
};
ostream& operator <<(ostream& stream, const BaseMessage &objectArg)
{
return objectArg.print(stream);
}
答案 2 :(得分:3)
流运营商喜欢:
virtual ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;
根本不能成员函数,所以不能成为虚函数。这样做的原因是当你说出这样的话时:
a << b;
你真的在说
a.operator<<( b );
在这种情况下,a是一个流,而不是您的类的实例,因此运算符不能是您的类的成员。您通常应该使它成为一个免费(非成员)函数,它通过合适的成员函数访问您的类实例。
答案 3 :(得分:1)
运算符&lt;&lt;()的声明是错误的。对于op&lt;&lt;的二进制版本,您不必声明第二个参数 - 如果op&lt;&lt;&lt;&lt;&lt;&lt;是该类的成员函数:
this
此外,正如其他人所提到的,流插入运算符必须是全局函数。使它们成为成员函数是行不通的。
也不是与你的问题无关的东西,但仍然是一个问题。在原始实现中,您按值传递了抽象基类对象,而不是通过引用或指针传递。执行此操作时,您“切片对象”。我敢肯定,你的意图是将一个基类指针传递给函数的多态类型,然后让函数调用方法多态。例如,您尝试执行与此类似的操作:
virtual ostream& operator<<(ostream& stream) = 0;
...并期望输出为“Der”但实际上由于Object Slicing,输出为“Base”。由于DumpIt()函数按值获取Base对象,因此将根据原始对象创建新的临时Base对象。为了获得您期望的功能,您需要通过引用或指针传递:
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
virtual void dump()
{
cout << "Base";
};
};
class Der : public Base
{
public:
void dump()
{
cout << "Der";
};
};
void DumpIt(Base b)
{
b.dump();
}
int main()
{
Der obj;
DumpIt(obj);
return 0;
}
此功能的输出为“Der”。
答案 4 :(得分:0)
这里的问题是“BaseMessage objectArg”表示objectArg对象应该按值传递。
这是不可能的,因为您已使用纯虚拟调用使类成为抽象。引用传递“BaseMessage&amp; objectArg”或通过指针“BaseMessage * objectArg”传递将使此错误消失。
按值传递意味着它使用复制构造函数创建变量的新实例。由于您已确定无法创建BaseMessage实例,因此无法完成此操作。