开发者

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).

it has a base class Object, and some derived classes : Long,Int,String,...,all derived classes has a no-virtual function "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 w开发者_如何学编程ant to add a function ,say, toString ,which can convert Object to a string:

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.

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++?

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. 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#:

#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).

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.,


No, you can't write toString in Object using a virtual 'value' function and override the return type. However you can write a virtual toString and with a template programming trick accomplish almost the same thing.

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. 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:

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


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.


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 (i.e virtual std::string getStringValue(), which gives you a string if applicable).
  • Use templates, if the type is known by the user.


Regarding @MerickOWA comment, here's another solution, that does not requires any additional template mechanism.

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.

#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...). Since this is standard C++ practice, it should not matter.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜