所有。我是C ++的新手,我正在用C ++编写一个小型库(主要用于我自己的项目)。在设计类型层次结构的过程中,我遇到了定义赋值运算符的问题。
我采用了最终在this article中达到的基本方法,即对于从类MyClass
派生的层次结构中的每个类Base
,您定义了两个赋值运算符,如这样:
class MyClass: public Base {
public:
MyClass& operator =(MyClass const& rhs);
virtual MyClass& operator =(Base const& rhs);
};
// automatically gets defined, so we make it call the virtual function below
MyClass& MyClass::operator =(MyClass const& rhs);
{
return (*this = static_cast<Base const&>(rhs));
}
MyClass& MyClass::operator =(Base const& rhs);
{
assert(typeid(rhs) == typeid(*this)); // assigning to different types is a logical error
MyClass const& casted_rhs = dynamic_cast<MyClass const&>(rhs);
try {
// allocate new variables
Base::operator =(rhs);
} catch(...) {
// delete the allocated variables
throw;
}
// assign to member variables
}
我关注的部分是类型相等的断言。由于我正在编写一个库,其中断言可能是从最终结果中编译出来的,这使我开始采用看起来更像这样的方案:
class MyClass: public Base {
public:
operator =(MyClass const& rhs); // etc
virtual inline MyClass& operator =(Base const& rhs)
{
assert(typeid(rhs) == typeid(*this));
return this->set(static_cast<Base const&>(rhs));
}
private:
MyClass& set(Base const& rhs); // same basic thing
};
但我一直想知道是否可以在编译时检查类型。我查看了Boost.TypeTraits,接近我做了BOOST_MPL_ASSERT((boost::is_same<BOOST_TYPEOF(*this), BOOST_TYPEOF(rhs)>));
,但由于rhs被声明为对父类的引用而不是派生类,因此它会被阻塞。
现在我考虑一下,我的推理似乎很愚蠢 - 我希望由于函数是内联的,它可以自己检查实际参数,但当然预处理器总是在编译器之前运行。但是我想知道是否有人知道我可以在编译时强制执行这种检查。
答案 0 :(得分:7)
您无法在编译时执行此断言,原因很简单,直到运行时才会知道运行时类型。
assert(typeid(rhs) == typeid(*this));
return this->set(static_cast<Base const&>(rhs));
在非内联版本中,您有dynamic_cast
。如果你的断言被违反,我会保留这个以便你得到一个明确定义的错误而不是未定义的行为。
如果你这样做,断言要么过于严格,要么毫无意义。 dynamic_cast
将在调试和发布版本中抛出bad_cast
异常。这就是你想要的。
就个人而言,我会质疑整个多态分配问题。我会遵循Scott Meyers的Effective C ++建议,并在继承层次结构中抽象所有非叶子节点。然后,您可以使基类赋值运算符受保护且非虚拟。
这使您可以在派生类赋值运算符中使用它们的实现,但可以防止客户端对对象进行切片。如果客户端类只有一个基类引用或指针,那么无论如何都应该尝试分配给该类是有疑问的。如果这样做,他们应该负责铸造和类型安全保障。
答案 1 :(得分:0)
如果您希望在编译时确定事物,那么动态多态(虚函数等)不适合您。我通常不认为需要将动态多态性与“正常值类型”之类的东西混合 - 就像赋值运算符一样。也许具体和非多态类是最适合您的情况。但是很难说,因为你没有说过你要对这些课程做些什么。