簡體   English   中英

在C ++中覆蓋虛函數時可以更改返回類型嗎?

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

我遇到了關於覆蓋虛函數的問題,實際上,它是關於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());  
       }  

   };  

但我不能寫一個虛值函數,因為返回值不能被覆蓋。

這個問題有什么好的解決方案嗎?

謝謝

在C ++中覆蓋虛函數時可以更改返回類型嗎?

僅以非常有限的方式,在(原始)指針或引用返回類型中可以是協變的。

這個問題有什么好的解決方案嗎?

嗯,有兩個相當不錯的解決方案,一個稍微不好的解決方案。

我在這里給你一個稍微不好的解決方案。 我給出的一個原因是它很容易理解,或者至少它很容易“復制和修改”,即使一個人不太了解它。 另一個原因是,其中一個好的解決方案需要一些廣泛的通用支持機制,這里沒有討論的余地,另一個好的解決方案(我認為幾乎在所有方面都是最好的解決方案)是至少一種,至少當我提出這種解決方案時,已經自動收到了驅動器的支持,只有那個,這里就是SO。 我猜這是為這里的多樣性付出的代價,多樣性是一件非常好的事情:-)但不幸的是,這意味着提供真正的好東西是沒有意義的,那么我就會接受負面的代表。

無論如何,代碼,基於虛擬繼承的優勢; 它與在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類等非常相似,可以考慮僅定義一個類模板,或者可能繼承此類模板的特化(這可能也有助於避免錯誤,因為它可以減少冗余)。

編輯 :我現在看到你已經接受了一個答案,這個答案基本上只是我關於模板的最后一個建議。 這意味着我已經回答了所提出的問題(針對不同類別的不同類別的解決方案),而你的內容則不那么通用。 那好吧。

干杯&hth。,

不,您不能使用虛擬“值”函數在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;

不幸的是,你不能通過返回值重載C ++中的函數。 你可以做什么,如果你有適合所有類型的some_convert_function就可以創建一個看起來像這樣的模板函數:

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

關於@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 ++實踐,因此無關緊要。

您不能使用不同的返回類型覆蓋函數; 最接近的是隱藏父類中的函數,在派生類中使用不同的函數。 但那不是你想要的,因為兩者將是不同的功能,完全不相關。

假設您需要在每個派生類中創建一個新的toString函數,這是正確的 - 這就是多態性的全部內容。

我不認為你是正確的方式。 雖然在某些情況下可以更改虛函數的返回類型,但請考慮:您的函數是如何使用的? 如果它是虛擬的,那么用戶將使用基類進行更改。 因此,他們不知道你的班級的實際類型是什么,因此他們不知道期望什么類型。 所以:

  • 返回基類類型。
  • 返回給你正確類型的函數(即virtual std::string getStringValue() ,如果適用,它會給你一個字符串)。
  • 如果用戶已知類型,請使用模板。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM