我遇到有关覆盖虚函数的问题,实际上,它是关于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());
}
};
但由于返回值无法覆盖,我无法编写虚拟值函数。
这个问题有什么好的解决方案吗?
谢谢
答案 0 :(得分:8)
仅以非常有限的方式,在(原始)指针或引用返回类型中可以是协变的。
嗯,有两个相当不错的解决方案,一个稍微不好的解决方案。
我在这里给你一个稍微不好的解决方案。我给出的一个原因是它很容易理解,或者至少它很容易“复制和修改”,即使一个人不太了解它。另一个原因是,其中一个好的解决方案需要一些广泛的通用支持机制,这里没有讨论的余地,而另一个好的解决方案(我认为几乎在所有方面都是最好的解决方案)是至少一种,至少当我提出这种解决方案时,已经自动收到了驱动器的支持,只有那个,这里就是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;
}
如果您的Long
和Int
类等非常相似,请考虑仅定义一个类模板,或者可能继承此类模板的特化(这可能也有帮助避免错误,因为它减少了冗余)。
编辑:我现在看到你已经接受了一个答案,这个答案基本上只是我关于模板的最后一个建议。这意味着我已经回答了所提出的问题(针对不同类别的不同类别的解决方案),而你的内容则不那么通用。哦,好吧。
干杯&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 ++实践,因此无关紧要。