一个好的设计可以完全避免铸件吗?

Can casts be completely avoided with a good design?

提问人:Luchian Grigore 提问时间:12/12/2011 最后编辑:TylerHLuchian Grigore 更新时间:4/16/2022 访问量:795

问:

我想知道是否存在完全需要铸造的情况。
我在这里谈论的是类之间的强制转换,而不是基本类型。

强制转换(无论是 C++ 风格的 or ,还是普通的 C 样式强制转换)是代码的味道吗?我可以看到有时它很有帮助,但我认为它也可以避免。投射会违反任何 OOP 规则吗?static_castdynamic_cast

C++ OOP 转换

评论

0赞 Pubby 12/12/2011
完全避免石膏并不是一个明智的决定。好的设计可以最小化它们,但避免它们会导致重新发明更糟糕的类型转换形式。

答:

4赞 Puppy 12/12/2011 #1

不是特别。应尽可能避免强制转换,但在最基本的级别上,C++ 的某些区域存在于类型安全领域之外,并且强制转换是必要的。 是一个特殊的例外,具体来说,即使在坚实的 OOP 设计中,它也可能是必要的。dynamic_cast

有“那不完美”,也有“OMGWTF,Y U SO DUMB”。演员阵容并不完美。

评论

0赞 Luchian Grigore 12/12/2011
您能举例说明需要演员表的情况吗?
0赞 Kerrek SB 12/12/2011 #2

典型的例子是 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;

评论

0赞 Luchian Grigore 12/12/2011
我指的是类之间的强制转换,而不是基本类型。
2赞 David Rodríguez - dribeas 12/12/2011 #3

如果代码气味是指它应该在代码审查中引发一个标志,那么它们就是代码气味。如果你的意思是它们永远不应该出现在代码中,那么不,强制转换有一些很好的用法。

举一个有趣的例子(我总是觉得类型擦除很有趣),看一下需要安全读取存储值的位置的实现(不像 s 必须猜测类型并且受到限制)boost::anydynamic_castunion

素描:

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;
}

评论

0赞 Gaff 12/12/2011
如果你想让任何拥有数据的人成为私人成员,你会如何解决这个问题,现在any_cast必须是朋友,但这不可能奏效,因为类型 T 在任何......那么,这是否意味着any::d ata必须始终是公开的呢?
0赞 Bowie Owens 12/12/2011 #4

我想说的是,好的设计不能完全避免铸造,因为在合理的情况下,铸造是一个不错的选择。代理非常有用,通常依赖于隐式或显式转换,例如下面进行惰性评估。

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

有时您的程序需要复杂的代码。你不能总是只坚持语言的简单、基本或“干净”的元素。