简体   繁体   English

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

[英]it is possible to change return type when override a virtual function in C++?

I encounter a problems about override virtual functions, in fact,it is about hessian (a web service protocol). 我遇到了关于覆盖虚函数的问题,实际上,它是关于hessian(一种Web服务协议)。

it has a base class Object, and some derived classes : Long,Int,String,...,all derived classes has a no-virtual function "value" 它有一个基类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_;  
       ...  
   };  

now I want to add a function ,say, toString ,which can convert Object to a string: 现在我想添加一个函数,比如toString,它可以将Object转换为字符串:

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

if I can change the value function to virtual ,I only need to write a toString function in Object, else, I need to write a virtual function toString, and to override this functions in all derived classes. 如果我可以将值函数更改为virtual,我只需要在Object中编写一个toString函数,否则,我需要编写一个虚函数toString,并在所有派生类中重写这个函数。

for example 例如

   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());  
       }  

   };  

but I can't write a virtual value function because of return value can't be override. 但我不能写一个虚值函数,因为返回值不能被覆盖。

is there any good solution for this issue? 这个问题有什么好的解决方案吗?

Thanks 谢谢

it is possible to change return type when override a virtual function in C++? 在C ++中覆盖虚函数时可以更改返回类型吗?

Only in a very limited way, in that (raw) pointer or reference return type can be covariant. 仅以非常有限的方式,在(原始)指针或引用返回类型中可以是协变的。

is there any good solution for this issue? 这个问题有什么好的解决方案吗?

Well, there are two fairly good solutions, and one slightly bad solution. 嗯,有两个相当不错的解决方案,一个稍微不好的解决方案。

I'm giving you the slightly bad solution here. 我在这里给你一个稍微不好的解决方案。 One reason that I'm giving that is that it's easy to understand, or at least it's quite easy to "copy and modify" even if one doesn't quite understand it. 我给出的一个原因是它很容易理解,或者至少它很容易“复制和修改”,即使一个人不太了解它。 Another reason is that one of the good solutions requires some extensive general support machinery that there's no room for discussing here, and the other good solution (the one that in my opinion is best in nearly every respect) is of a kind that, at least when I have presented that kind of solution, has automatically received drive-by downvotes and only that, here on SO. 另一个原因是,其中一个好的解决方案需要一些广泛的通用支持机制,这里没有讨论的余地,另一个好的解决方案(我认为几乎在所有方面都是最好的解决方案)是至少一种,至少当我提出这种解决方案时,已经自动收到了驱动器的支持,只有那个,这里就是SO。 I guess that that's the price to pay for the diversity here, which diversity is a Very Good Thing :-) But, unfortunately it means that there's no point in offering the real good stuff, I'd be down to negative rep then. 我猜这是为这里的多样性付出的代价,多样性是一件非常好的事情:-)但不幸的是,这意味着提供真正的好东西是没有意义的,那么我就会接受负面的代表。

Anyways, code, based on dominance in virtual inheritance; 无论如何,代码,基于虚拟继承的优势; it's about the same as inheriting an implementation of an interface in Java or 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;
}

If your Long and Int classes etc. are very similar, as they seem to be, consider defining just one class template, or perhaps inherit from specializations of such a template (this might also help avoid bugs, since it reduces redundancy). 如果您的LongInt类等非常相似,可以考虑仅定义一个类模板,或者可能继承此类模板的特化(这可能也有助于避免错误,因为它可以减少冗余)。

EDIT : I see now that you have accepted an answer that is essentially just my last suggestion about templating. 编辑 :我现在看到你已经接受了一个答案,这个答案基本上只是我关于模板的最后一个建议。 That means that I've answered the question as posed (a solution for distinct, different classes) while you had something less general in mind. 这意味着我已经回答了所提出的问题(针对不同类别的不同类别的解决方案),而你的内容则不那么通用。 Oh well. 那好吧。

Cheers & hth., 干杯&hth。,

No, you can't write toString in Object using a virtual 'value' function and override the return type. 不,您不能使用虚拟“值”函数在Object中编写toString并覆盖返回类型。 However you can write a virtual toString and with a template programming trick accomplish almost the same thing. 但是你可以写一个虚拟的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;

Unfortunately you can't overload functions in C++ by return value. 不幸的是,你不能通过返回值重载C ++中的函数。 What you could do, if you have the appropriate some_convert_function in place for all types you need it for would be to create free a template function that looks something like this: 你可以做什么,如果你有适合所有类型的some_convert_function就可以创建一个看起来像这样的模板函数:

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

Regarding @MerickOWA comment, here's another solution, that does not requires any additional template mechanism. 关于@MerickOWA评论,这是另一种解决方案,不需要任何额外的模板机制。

Since you intended to have a virtual "value()" method that you needed to implement in all classes, I've extended the idea (usually, in these kind of framework, you've plenty of similar "basic" methods, so I've used a macro to write them for me, it's not required, it's just faster and less error prone. 既然你想要在所有类中实现一个虚拟的“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;
}

Obviously, the trick is in the std::ostringstream classes that's accept any parameter type (long long, long, etc...). 显然,技巧是在std :: ostringstream类中接受任何参数类型(long long,long等等)。 Since this is standard C++ practice, it should not matter. 由于这是标准的C ++实践,因此无关紧要。

You can't override a function with a different return type; 您不能使用不同的返回类型覆盖函数; the closest you can come is to hide a function in the parent with a different one in the derived class. 最接近的是隐藏父类中的函数,在派生类中使用不同的函数。 But that's not what you want, because the two will be different functions, completely unrelated. 但那不是你想要的,因为两者将是不同的功能,完全不相关。

You were correct in assuming that you'd need to create a new toString function in each derived class - that's what polymorphism is all about. 假设您需要在每个派生类中创建一个新的toString函数,这是正确的 - 这就是多态性的全部内容。

I don't think you're going about this the right way. 我不认为你是正确的方式。 While it is possible in some circumstances to change the return type of a virtual function, consider this: How is your function being used? 虽然在某些情况下可以更改虚函数的返回类型,但请考虑:您的函数是如何使用的? If it's virtual, changes are that users will be using the base class. 如果它是虚拟的,那么用户将使用基类进行更改。 As such, they are oblivious as to what the actual type of your class is, and thus they wouldn't know what type to expect. 因此,他们不知道你的班级的实际类型是什么,因此他们不知道期望什么类型。 So: 所以:

  • Either return the base class type. 返回基类类型。
  • Return functions that give you the proper type (ie virtual std::string getStringValue() , which gives you a string if applicable). 返回给你正确类型的函数(即virtual std::string getStringValue() ,如果适用,它会给你一个字符串)。
  • Use templates, if the type is known by the user. 如果用户已知类型,请使用模板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM