代码之家  ›  专栏  ›  技术社区  ›  camino

在C++中重写虚拟函数时,有可能改变返回类型吗?

  •  7
  • camino  · 技术社区  · 15 年前

    我遇到了一个关于重写虚拟函数的问题,实际上,它是关于hessian(一个web服务协议)的。

    它有一个基类对象和一些派生类: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 *obj  = ...
    cout<<obj->toString();
    

    如果我可以将value函数更改为virtual,我只需要在Object中编写一个toString函数,否则,我需要编写一个virtual函数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 回复  |  直到 15 年前
        1
  •  2
  •   MerickOWA    15 年前

    不,不能使用虚拟“value”函数在对象中写入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
  •  8
  •   Cheers and hth. - Alf    15 年前

    在C++中重写虚拟函数时,有可能改变返回类型吗?

    只有在非常有限的情况下,指针或引用返回类型才能协变。

    这个问题有什么好的解决办法吗?

    好吧,有两个相当好的解决方案,一个稍差的解决方案。

    我给你一个稍微不好的解决方案。我给出的一个原因是它很容易理解,或者至少它很容易“复制和修改”,即使一个人不太理解它。另一个原因是,一个好的解决方案需要一些广泛的一般性支持机制,这里没有讨论的余地,而另一个好的解决方案(在我看来,它在几乎所有方面都是最好的)是这样的,至少当我提出这种解决方案时,它自动地以否决票获得了支持只有这个,就这样。我想这是为多样性付出的代价,多样性是一件非常好的事情:-)但是,不幸的是,这意味着提供真正好的东西是没有意义的,那我就只能说是消极的。

    无论如何,代码,基于虚拟继承中的支配地位;这与用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;
    }
    

    如果你的 Long Int 类等看起来非常相似,可以考虑只定义一个类模板,或者可能继承自此类模板的专门化(这也可能有助于避免错误,因为它减少了冗余)。

    编辑 :现在我明白了,您已经接受了一个答案,这基本上只是我关于模板化的最后一个建议。这意味着我已经回答了提出的问题(一个针对不同类的解决方案),而你却有一些不太一般的想法。哦,好吧。

    干杯。,

        3
  •  1
  •   Timo Geusch    15 年前

    不幸的是,不能用返回值重载C++中的函数。如果你有合适的 some_convert_function 对于所有需要它的类型,都需要创建一个如下所示的免费模板函数:

    template<typename T>
    std::string toString(T const& t)
    {
      return some_convert_function<T>(t);
    }
    
        4
  •  0
  •   Mark Ransom    15 年前

    不能用不同的返回类型重写函数;最接近的方法是在父类中用派生类中的不同类型隐藏函数。但这不是你想要的,因为这两种功能是不同的,完全不相关的。

    你认为你需要创建一个新的 toString 每个派生类中的函数-这就是多态性的意义所在。

        5
  •  0
  •   EboMike    15 年前

    我认为你的做法不对。虽然在某些情况下可以更改虚拟函数的返回类型,但请考虑以下问题:如何使用您的函数?如果是虚拟的,则更改为用户将使用基类。因此,他们不知道你的类的实际类型是什么,因此他们不知道期望的类型是什么。所以:

    • 返回基类类型。
    • 返回给您正确类型的函数(即 virtual std::string getStringValue() ,如果适用,则为您提供一个字符串)。
    • 如果用户知道类型,请使用模板。
        6
  •  0
  •   xryl669    12 年前

    关于@MerickOWA comment,这里有另一个解决方案,它不需要任何额外的模板机制。

    因为您打算在所有类中实现一个虚拟的“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++实践,那就不重要了。

    推荐文章