如何覆盖ostream<<已定义类型的运算符

时间:2013-06-16 10:25:26

标签: c++ inheritance override

我目前正在使用现有库的端口,该库使用ostream写入终端。

ostream是作为港口的一部分派生的。

使用的ostream衍生类定义如下:

class owstream: public std::ostream {
public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

这用于输出一些数据,通常是整数和双精度数。

问题是我正在处理的平台有一个错误的双打印例程导致内核崩溃。

所以在某些情况下,如果我这样做:

ostream* mystream = new ostream(...);
(*mystream) << 1.23456

KABOOM

所以,我试图覆盖&lt;&lt;特定类型的运算符,如:

ostream* GetStream() { return = new MyOStream(...); }

....

ostream* mystream = GetStream()
mystream << 1.23456;

不幸的是,运营商&lt;&lt; ostream中的成员不是虚拟的,所以如果我创建了一个重写的运算符&lt;&lt;会员永远不会被召唤。

我尝试用以下内容扩展它:

class owstream: public std::ostream {
    friend std::ostream& operator<<(std::ostream& out, double val);
  public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

extern std::ostream& operator<<(std::ostream &out, double val);

但是这会导致关于operator&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;显而易见,显然这是一个已由基础iostream类处理的类型。

我开始怀疑这是否可能。

你将如何实现&lt;&lt;运算符在给定特定的已处理类型时覆盖行为?

目标是能够做到这样的事情:

cout << 1.234567

没有崩溃(我显然不会使用cout,但是如上定义的GetStream()可以很好地返回cout)

2 个答案:

答案 0 :(得分:1)

这样的事情:

template <typename T>
owstream& operator<<(owstream&, const T&);

template <typename T>
inline owstream&
operator<<(owstream& os, const T& val)
{
   std::ostream& stdos = static_cast<std::ostream&>(os);
   stdos << val;
   return os;
}

template <>
inline owstream&
operator<<(owstream& os, const double& val)
{
   std::stringstream ss;
   ss << val;
   os << ss.str();
   return os;
}

答案 1 :(得分:1)

我发现您当前的方法/实施存在三个问题。

问题#1

即使您成功地使用std::ostream作为第一个参数并返回值,它也无法正常工作。问题主要源于从重载的ostream返回operator<<。将第一个值发送到owstream后,所有后续值都会发送到返回的ostream。这会让您回到原始问题所在的位置。为了使其正常工作,您需要将owstream作为第一个参数,并在重载的owstream中返回operator<<

问题#2

另一个问题是owstream可以隐式转换为std::ostream。根据项目中owstream的使用方式,您提供的重载可能在某些情况下无法发挥作用。例如,如果将owstream类型的对象传递给接受std::ostream的函数,则可能最终遇到您当前遇到的问题。您可以使用private继承来防止这种情况发生。这样可以防止将owstream隐式用作std::ostream。使用private继承还可以防止您在std::ostream中无意中使用可能导致您回到原始问题的函数。对于绝对需要std::ostream的情况,您可以使用访问器函数显式检索对它的引用。

问题#3

最后一个问题是std::ostream包含operator<<的重载,它处理std::ostream特定的IO操纵符,例如std::endl。如果你没有提供过载来专门处理这些问题,那么std::ostream中的那个会被使用,你会再一次回到你开始的地方。如果使用上面描述的private继承并且不提供处理操纵器的重载,则无法编译。


<强>解决方案

下面的解决方案类似于JRG提供的解决方案,包括doublefloatstd::ostream IO操纵器的访问器功能和重载。这是一个完整的工作示例,为简单起见,使用std::cout的流缓冲区。我包含了float的重载,因为缺陷库实现可能与它们有类似的问题。它还使用私有继承来防止隐式转换为std::ostream

我在VC ++ 10和GCC 4.7.2上测试过它。您可能需要根据编译器和库的兼容性进行一些调整。

#include <ostream>
#include <iostream>
#include <sstream>

class owstream : private std::ostream
{
public:
    owstream(std::streambuf* sb)
        : std::ostream(sb)
    {}

    // Template based operator...ohhhhhh ahhhhh.
    template <typename T>
    friend owstream& operator<<(owstream&, const T&);

    // Additional overload to handle ostream specific io manipulators 
    friend owstream& operator<<(owstream&, std::ostream& (*)(std::ostream&));

    // Accessor function to get a reference to the ostream
    std::ostream& get_ostream() { return *this; }
};


template <typename T>
inline owstream&
operator<<(owstream& out, const T& value)
{
    static_cast<std::ostream&>(out) << value;
    return out;
}

//  overload for double
template <>
inline owstream&
operator<<(owstream& out, const double& value)
{
    std::stringstream ss;
    ss << value;
    return out << ss.str();
}

//  overload for float
template <>
inline owstream&
operator<<(owstream& out, const float& value)
{
    std::stringstream ss;
    ss << value;
    return out << ss.str();
}

//  overload for std::ostream specific io manipulators
inline owstream&
operator<<(owstream& out, std::ostream& (*func)(std::ostream&))
{
    static_cast<std::ostream&>(out) << func;
    return out;
}

int main()
{
    owstream ows(std::cout.rdbuf());

    ows << std::endl;
    ows << "hello " << 1.0 << " " << 2.0f << std::endl;
}