在C ++中覆盖虚函数时可以更改返回类型吗?

时间:2010-11-19 05:11:07

标签: c++ function override virtual

我遇到有关覆盖虚函数的问题,实际上,它是关于hessian(一种Web服务协议)。

它有一个基类Object,以及一些派生类:Long,Int,String,...,所有派生类都有一个无虚函数“value”

   class Object  
   {  
     ...    
   };  


   class Long :public Object  
   {  
       ...  
   public:  
       typedef long long  basic_type;  
       basic_type value(){return value_;}  
   private:  
       basic_type value_;  
       ...  
   };  


   class Int :public Object  
   {  
      ...  
   public:  
       typedef int basic_type;  
       basic_type value(){return value_;}  
   private:   
       basic_type value_;  
       ...  
   };  

现在我想添加一个函数,比如toString,它可以将Object转换为字符串:

Object *obj  = ...
cout<<obj->toString();

如果我可以将值函数更改为virtual,我只需要在Object中编写一个toString函数,否则,我需要编写一个虚函数toString,并在所有派生类中重写这个函数。

例如

   class Object  
   {  
       virtual Type value(); // It seemed that I can't write a function like this,because the Type is different for different derived classes  


       std::string toString()  
       {  
           some_convert_function(value());  
       }  

   };  

但由于返回值无法覆盖,我无法编写虚拟值函数。

这个问题有什么好的解决方案吗?

谢谢

6 个答案:

答案 0 :(得分:8)

在C ++中覆盖虚函数时可以更改返回类型吗?

仅以非常有限的方式,在(原始)指针或引用返回类型中可以是协变的。

这个问题有什么好的解决方案吗?

嗯,有两个相当不错的解决方案,一个稍微不好的解决方案。

我在这里给你一个稍微不好的解决方案。我给出的一个原因是它很容易理解,或者至少它很容易“复制和修改”,即使一个人不太了解它。另一个原因是,其中一个好的解决方案需要一些广泛的通用支持机制,这里没有讨论的余地,而另一个好的解决方案(我认为几乎在所有方面都是最好的解决方案)是至少一种,至少当我提出这种解决方案时,已经自动收到了驱动器的支持,只有那个,这里就是SO。我想这就是为这里的多样性付出的代价,多样性是一件非常好的事情:-)但不幸的是,这意味着提供真正的好东西是没有意义的,那么我会接受负面的代表。 / p>

无论如何,代码,基于虚拟继承的主导地位;它与继承Java或C#中的接口实现大致相同:

#include <iostream>
#include <string>
#include <sstream>

//--------------------------------------- Machinery:

class ToStringInterface
{
public:
    virtual std::string toString() const = 0;
};

template< typename ValueProvider >
class ToStringImpl
    : public virtual ToStringInterface
{
public:
    virtual std::string toString() const
    {
        ValueProvider const&    self    =
            *static_cast<ValueProvider const*>( this );
        std::ostringstream      stream;
        stream << self.value();
        return stream.str();
    }
};

//--------------------------------------- Usage example:

class Object  
    : public virtual ToStringInterface
{  
    // ...    
};  

class Long
    : public Object
    , public ToStringImpl< Long >
{  
public:  
   typedef long long  BasicType;  
   Long( BasicType v ): value_( v ) {}
   BasicType value() const { return value_; }  
private:  
   BasicType value_;  
};  

class Int
    : public Object
    , public ToStringImpl< Int >
{  
public:  
   typedef int BasicType;  
   Int( BasicType v ): value_( v ) {}
   BasicType value() const { return value_; }
private:   
   BasicType value_;  
}; 

int main()
{
    Object const& obj = Int( 42 );
    std::cout << obj.toString() << std::endl;
}

如果您的LongInt类等非常相似,请考虑仅定义一个类模板,或者可能继承此类模板的特化(这可能也有帮助避免错误,因为它减少了冗余)。

编辑:我现在看到你已经接受了一个答案,这个答案基本上只是我关于模板的最后一个建议。这意味着我已经回答了所提出的问题(针对不同类别的不同类别的解决方案),而你的内容则不那么通用。哦,好吧。

干杯&amp;第h。,

答案 1 :(得分:2)

不,您不能使用虚拟'value'函数在Object中编写toString并覆盖返回类型。但是你可以编写一个虚拟的toString,并使用模板编程技巧完成几乎相同的事情。

class Object
{
public:
  virtual std::string toString();
}


template < class ValueType >
class BasicType : Object
{
  public:
  typedef ValueType basic_type;
  basic_type value() { return value_; }

  std::string toString()
  {
    return some_convert_function( value_ );
  }

  private:
  basic_type value_;
}

typedef BasicType<long long> Long;
typedef BasicType<int>       Int;

答案 2 :(得分:1)

不幸的是,你不能通过返回值重载C ++中的函数。你可以做什么,如果你有适合所有类型的some_convert_function就可以创建一个看起来像这样的模板函数:

template<typename T>
std::string toString(T const& t)
{
  return some_convert_function<T>(t);
}

答案 3 :(得分:0)

您不能使用不同的返回类型覆盖函数;最接近的是隐藏父类中的函数,在派生类中使用不同的函数。但这不是你想要的,因为这两者将是不同的功能,完全不相关。

你认为你需要在每个派生类中创建一个新的toString函数是正确的 - 这就是多态性的全部内容。

答案 4 :(得分:0)

我认为你不是正确的方式。虽然在某些情况下可以更改虚函数的返回类型,但请考虑:您的函数是如何使用的?如果它是虚拟的,那么用户将使用基类进行更改。因此,他们不知道你的班级的实际类型是什么,因此他们不知道期望什么类型。所以:

  • 返回基类类型。
  • 返回给您正确类型的函数(例如virtual std::string getStringValue(),如果适用,它会为您提供一个字符串。)
  • 如果用户已知类型,请使用模板。

答案 5 :(得分:0)

关于@MerickOWA评论,这是另一个解决方案,不需要任何额外的模板机制。

由于你打算在所有类中实现一个虚拟的“value()”方法,我已经扩展了这个想法(通常,在这种框架中,你有很多类似的“基本”方法,所以我使用宏来为我编写它们,它不是必需的,它只是更快,更不容易出错。

#include <iostream>
#include <string>
#include <sstream>

struct Object
{
   std::string toString() const { std::ostringstream str; getValue(str); return str.str(); }
   virtual void getValue(std::ostringstream & str) const { str<<"BadObj"; }
};

// Add all the common "basic & common" function here
#define __BoilerPlate__     basic_type value; void getValue(std::ostringstream & str) const { str << value; }
// The only type specific part
#define MAKE_OBJ(T)         typedef T basic_type;      __BoilerPlate__

struct Long : public Object
{
   MAKE_OBJ(long long)
   Long() : value(345) {}
};

struct Int : public Object
{
   MAKE_OBJ(long)
   Int() : value(3) {}
};

int main()
{
    Object a;
    Long b;
    Int c;
    std::cout<<a.toString()<<std::endl; // BadObj
    std::cout<<b.toString()<<std::endl; // 345
    std::cout<<c.toString()<<std::endl; // 3
    return 0;
}

显然,技巧是在std :: ostringstream类中接受任何参数类型(long long,long等等)。由于这是标准的C ++实践,因此无关紧要。