给定一个C ++基类指针,有没有办法检测是否已覆盖某个虚方法?
class Value {
public:
virtual bool CanBeString() const { return false; }
virtual std::string GetAsString() const {
throw std::logic_error("Value cannot be represented as a string.");
}
virtual bool CanBeInt() const { return false; }
virtual int GetAsInt() const {
throw std::logic_error("Value cannot be represented as an int.");
}
};
class StringValue : public Value {
public:
bool CanBeString() const override { return true; }
std::string GetAsString() const override { return m_string; }
private:
std::string m_string;
};
class IntValue : public Value {
public:
// Even though this is an integer, it can be represented as a string.
bool CanBeString() const override { return true; }
std::string GetAsString() const override { /* return string rep of m_int */ }
bool CanBeInt() const override { return true; }
int GetAsInt() const override { return m_int; }
private:
int m_int;
};
解释器使用指向Value
的指针,并在运行时进行类型检查。假设解释器有一个值,它需要对它执行一个仅适用于int的操作。它将首先检查pValue->CanBeInt()
。如果为true,则解释器继续pValue->GetAsInt()
并进行处理。如果该值与int不兼容,则报告类型错误。
永远不应执行GetAsXxx
的基本实现。如果执行了一个,则表示解释器中存在错误(它忘记首先检查类型)。那些抛出的声明是用来指示我修复解释器的;我 not 想要抛出异常,因为代码中的类型错误被解释。
为了更容易(并且不易出错)向系统添加新类型,我一直在尝试确定是否有办法消除派生类覆盖的需要CanBeXxx
能够检测是否已覆盖相应的GetAsXxx
方法。
我的具体想法是将CanBeXxx
方法更改为基类中定义的非虚方法,尝试将GetAsXxx
方法的成员函数指针与Value::GetAsXxx
方法的成员函数指针进行比较}。如:
bool CanBeInt() const { return &GetAsInt != &Value::GetAsInt; }
唉,这不会编译,因为很明显你无法获得已经绑定的方法的成员函数指针。这个想法或其他方法是否有变化可以允许这一小部分反射?
答案 0 :(得分:1)
拉开成员方法指针以进行比较是特定于编译器的,因为不同的编译器以不同方式处理方法指针。但是,您可以考虑使用其他设计。它不会完全消除这个问题,但它会简化一点,同时仍然可以灵活地在未来添加更多类型:
const unsigned int CanBeInt = 1;
const unsigned int CanBeString = 2;
...
class Value {
private:
unsigned int flags;
public:
Value(unsigned int aflags) : flags(aflags) {}
unsigned int GetFlags() const { return flags; }
inline bool CanBeString() const { return (flags & CanBeString); }
virtual std::string GetAsString() const {
throw std::logic_error("Value cannot be represented as a string.");
}
inline bool CanBeInt() const { return (flags & CanBeInt); }
virtual int GetAsInt() const {
throw std::logic_error("Value cannot be represented as an int.");
}
};
class StringValue : public Value {
public:
StringValue() : Value(CanBeString) {}
std::string GetAsString() const override { return m_string; }
private:
std::string m_string;
};
class IntValue : public Value {
public:
// Even though this is an integer, it can be represented as a string.
IntValue() : Value(CanBeInt | CanBeString) {}
std::string GetAsString() const override { /* return string rep of m_int */ }
int GetAsInt() const override { return m_int; }
private:
int m_int;
};
if (pValue->CanBeInt()) {
int val = pValue->GetAsInt();
...
}
if (pValue->GetFlags() & CanBeInt) {
int val = pValue->GetAsInt();
...
}
if (pValue->CanBeString()) {
std::string val = pValue->GetAsString();
...
}
if (pValue->GetFlags() & CanBeString) {
std::string val = pValue->GetAsString();
...
}
另一个选项,如@Beta建议:
class Value {
public:
virtual bool GetAsString(std::string &) const { return false; }
virtual bool GetAsInt(int &) const { return false; }
};
class StringValue : public Value {
public:
bool GetAsString(std::string &value) const override { value = m_string; return true; }
private:
std::string m_string;
};
class IntValue : public Value {
public:
// Even though this is an integer, it can be represented as a string.
bool GetAsString(std::string &value) const override { value = ...; return true; }
bool GetAsInt(int &value) const override { value = m_int; return true; }
private:
int m_int;
};
std::string val;
if (!pValue->GetAsString(val))
throw std::logic_error("Value cannot be represented as a string.");
int val;
if (!pValue->GetAsInt(val))
throw std::logic_error("Value cannot be represented as a int.");