我想知道是否存在完全需要铸造的情况。
我在这里谈论的是类之间的转换,而不是基本类型。
强制转换(无论是像static_cast
or的 C++ 风格dynamic_cast
,还是普通的 C 风格强制转换)是一种代码味道吗?我可以看到它有时是有帮助的,但我认为它也可以避免。强制转换是否违反任何 OOP 规则?
我想知道是否存在完全需要铸造的情况。
我在这里谈论的是类之间的转换,而不是基本类型。
强制转换(无论是像static_cast
or的 C++ 风格dynamic_cast
,还是普通的 C 风格强制转换)是一种代码味道吗?我可以看到它有时是有帮助的,但我认为它也可以避免。强制转换是否违反任何 OOP 规则?
不是特别。应尽可能避免强制转换,但在最基本的层面上,C++ 的某些区域存在于类型安全领域之外,并且强制转换是必要的。dynamic_cast
是一个特殊的例外,特别是,即使在可靠的 OOP 设计中也可能需要它。
有“那不完美”和“OMGWTF,YU SO DUMB”。演员阵容并不完美。
如果通过代码异味你的意思是它应该在代码审查中提出一个标志,那么它们就是一种代码异味。如果您的意思是它们永远不应该出现在代码中,那么不,强制转换有一些很好的用途。
举一个有趣的例子(我总是觉得类型擦除很有趣),看看boost::any
where dynamic_cast
is required to safe read from the stored value 的实现(不像union
s 你必须猜测类型并且受到限制)
草图:
struct any_base {
virtual ~any_base() {}
};
template <typename T>
struct any_data : any_base {
T value;
any_data( T const & value ) : value(value) {}
};
struct any {
any_base * data;
any() : data() {}
~any() { delete data; }
template <typename T>
any( T const & v ) : data( new any_data<T>(v) {}
}
template <typename T>
T any_cast( any const & a ) {
any_base<T> * p = dynamic_cast< any_base<T>* >( a.data );
if ( !p ) throw invalid_cast();
return *p;
}
典型的例子是 I/O:这是进行强制转换的少数也是唯一的原因之一,它还使用了 C++ 中唯一合法的指针类型之一char *
::
uint32_t n;
infile.read(reinterpret_cast<char *>(&n), sizeof n);
n *= 2;
outfile.write(reinterpret_cast<const char *>)(&n), sizeof n);
其他“类似 I/O”的操作需要类似的模式,例如加密或编码转换。
(C++ 中的另一个合法指针是void *
在分配上下文中使用时,但它不需要强制转换:C++ 将内存指针“转换”为对象指针的方法是通过构造: void * addr = get_memory();
,然后T * p = new (addr) T;
。)
我想说,好的设计不能完全避免铸造,因为在某些合理的情况下,铸造是一个不错的选择。代理非常有用,通常依赖于隐式或显式转换,例如以下进行惰性评估的。
template <class fn_t, class result_t>
class lazy_t {
fn_t fn_;
public:
lazy_t(fn_t fn) : fn_ (fn) { }
operator result_t () { return fn_(); }
};
在这种情况下,编译器可以使用隐式转换来执行给定函数的惰性求值。我会认为转换运算符是类的公共接口的一部分。
在某些情况下,例如实现多分派,dynamic_cast<> 也是必需的。有关更多信息,请参见http://en.wikipedia.org/wiki/Multiple_dispatch。
有时您的程序需要复杂的代码。你不能总是只坚持语言的简单、基本或“干净”的元素。