6

我有两个班级,有两个 global friend oparator<<

class A {
    friend std::ostream& operator<<(std::ostream& o, const A &a);
};

class B: public A {
    friend std::ostream& operator<<(std::ostream& o, const B &b);
};

如果我这样使用它,一切正常,B运算符的版本被调用:

B b;
std::cout << b;

但是,如果我使用多态性,则会A调用该版本,尽管动态类型是B

A* b = new B();
std::cout << *b;

一种解决方案是铸造:

std::cout << static_cast<B&>(*b);

但是有没有更简单或更优雅的解决方案呢?

4

3 回答 3

10

是的。类中的一个输出运算符和virtual print函数。

class A
{
public:
   virtual ~A() {}
private:
   virtual void print(std::ostream&) {}
   friend std::ostream& operator << (std::ostream& os, const A& obj)
   {
      obj.print(os);
      return os;
   }
};

class B
{
private:
   virtual void print(std::ostream&) {}
};

活生生的例子

于 2013-04-03T11:35:58.877 回答
3

派生类中的函数版本只有在您通过指向基类的指针访问它们时才会被调用,如果您将它们定义为,virtual因为编译器不知道指针指向的对象的类实际上是什么。

这里的问题是您正在定义友元函数,因此它们本身不能是虚拟的,解决方案很简单:让 Aoperator<<调用一个虚拟函数的实现A,然后您可以重载 in B

于 2013-04-03T11:38:12.100 回答
2

只是不要使用朋友。它们是比继承更紧密的耦合。特别是如果您为类模板编写这些友元运算符,则可以对其进行专门化并合法地访问您的类的内部结构。由于这些原因,我仅将这些运算符用作语法糖,并让它们委托给执行实际工作的成员函数。这样,您的问题的解决方案就很容易了:

class A {
public:
  virtual std::ostream& printToStream(std::ostream& os) const;
};

std::ostream& operator<<(std::ostream& os, A const& a)
{ return a.printToStream(os); }

class B: public A {
  virtual std::ostream& printToStream(std::ostream& os) const;
};

有另一个从 A 派生的 C 类吗?没问题,不需要再次定义语法糖(即operator<<),只需定义实际工作的完成方式,即覆盖printToStream- 这就是我将其设为虚拟的原因。

于 2013-04-03T11:42:07.330 回答