C ++抽象类运算符重载和接口执行问题

时间:2010-01-13 18:05:39

标签: c++

(从原始帖子编辑,将“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运算符?

非常感谢,

史蒂夫

5 个答案:

答案 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实例,因此无法完成此操作。