作者cppOrz (cppOrz)
看板C_and_CPP
标题Re: [问题] static.const.reinterpret_cast和c-sty …
时间Sun Mar 12 23:26:23 2006
Sorry, 有很多地方错误,不得不指出来。
热心回答问题是好的,但是最好对内容有一定的把握,以免误导。
这一篇介绍 C++ 的四个关键字: static_cast, const_cast, reinterpret_cast,
以及 dynamic_cast 的意义用法。
学习任何编程语言,只单纯学习语法是不够的,要适当的运用某种语言机制,最好
要先知道该机制存在的目的,和什麽状况下会需要用到它,否则还不如不要使用。
※ 引述《godfat (godfat 真常)》之铭言:
: 静态转型,简单地说就是强制转型,
: 只是不合理的转型 compiler 会告诉你不能这样转
: 例如两个毫无关联的物件
: class A{};
: class B{};
: A a;
: static_cast<B>(a); // error
static_cast 用於「相关型别」之间的转换,其目的是让编译器知道,
程序员是「有意识」的进行转换动作,并非不小心打错。例如:
float f = 123.456f;
int i = f; // 把 float 转为 int,编译器一般会给警告
int v = static_cast<int>(f); // 用 static_cast 通知编译器,设计意途确实如此
此外 static_cast 也可用於继承体系内的 downcast,例如:
struct B { void f(); };
struct D : B { void g(); } d;
B &b = d;
...
b.f(); // ok
b.g(); // 错误,b 是 reference to B 物件
static_cast<D&>(b).g(); // ok, 因为程序员知道 b 确实是参照某个 D 物件
: : const_cast
: 单纯去掉物件的常数性 和/或 volatile 性
这里是对的。
: int const i = 10;
: i = 5; // error
: const_cast<int&>(i) = 5; // ok
: (这边我不太确定是不是这样写,很少用)
: (总之观念是这样)
举例有误。
const_cast 只是把常数性去掉,目的是方便设计,但是企图通过 const_cast
修改一个 const 物件,其结果未定义(视实作环境是否对该 const 物件采取
保护措施)。
至於 const_cast 使用的时机,通常是为了在不修改既有模组(例如没有 source
code 的程式库)的情况下,解决两个模组之间 const-correctness 不相容的问题
(通常是不能改的那个模组,其设计不够周延)。例如:
struct C { void f(); }; // 假设 C 是一个既存的模组(不能修改)
void foo(C const &c) // foo 是自行设计的模组,c 物件只作为输入,不会被更动
{
...
c.f(); // 由於 c 是 referece to const C 物件,但 C::f 并未被设计为
// const,因此这样写是不成立的。(编译器会给错误或至少警告)
const_cast<C&>(c).f(); // ok, 用 const_cast 去掉 c 的常数性
}
: : reinterpret_cast
: 强迫 compiler 把输入型别视为欲转换的型别
: 这个不太好解释…总之就是暴力转型就对了 XD
: 直接把该记忆体位置的资料视为欲转换的型别来看待
reinterpret_cast 几乎等於暴力转型(指 C-style 转型),但不能去掉物件
的常数性(也就是还不够暴力)。它用来处理任意型别之间的转换,通常是为
了争取运算或储存空间的效率时,所采取的低阶操作。例如:
int IP_Addr = 0;
char *p = reinterpret_cast<char*>(&IP_Addr); // 转型为 char *,以便於
// 以 Byte 为单位来处理
p[0] = 192;
p[1] = 168;
p[2] = 0;
p[3] = 1;
传统的 C-style 转型相当於 static_cast, const_cast, reinterpret_cast
三合一(也就是暴力加三级)。
: 你漏了 dynamic_cast<>
: 这跟 static_cast<> 有些类似
dynamic_cast 和 static_cast 无关。
: dynamic_cast<> 只能对於具有多形性型别转型,
: 也就是他至少得要有一个 virtual function
这里是对的。dynamic_cast 是 RTTI 的一部份,它用来检测一个多型基底
类别的 pointer 或 reference 所参照物件的实际型别。例如:
struct B1 { virtual void f() = 0; };
struct D1 { void f(); };
struct X : D1
{
void f();
void g();
};
void foo(B1 *b1) // foo 模组并不知道 b1 所参照物件的实际型别
{
if (dynamic_cast<D1*>(b1)) // 如果 b1 参照的是 D1 物件
{
b1->f();
}
else if (D1 *d1 = dynamic_cast<X*>(b1)) // 如果 b1 参照的是 X 物件
{
d1->g();
}
else
{
...
}
}
此例中,dynamic_cast 的用法也是所谓的 downcast,但和 static_cast
不同的是,前者用於侦测未知多型物件的实际型别,而後者用於对已知物件
(可以是多型物件或普通物件)的转型。
可以看出,B1, D1, X 的继承体系设计并不是很理想,因为在这种情况下,应该
直接利用 virtual function 的动态多型机制,而非依赖 dynamic_cast 侦测物
件的型别。例如:
struct B2
{
virtual void f() = 0;
virtual void g() = 0;
};
struct D2 : B2
{
void f();
void g();
} d2;
struct Y : D2
{
void f();
void g();
} y;
void foo()
{
B2 *b2 = &d2; // b2 参照 D2 物件
b2->g(); // 实际上会执行 D2::g,不必依赖 dynamic_cast
b2 = &y; // 换一下,改参照 Y 物件
b2->g(); // 实际上会执行 Y::g
}
显然,B2, D2, Y 的设计比较合理。事实上,如果所有的模组都是自行设计,
dynamic_cast 是多余的。但它之所以存在,其目的就是为了解决「既存模组
」不能修改的问题。(最常见的例子,就是缺乏源代码的程式库)
前面的例子中,如果 B1, D1 两模组不能修改,为了新扩充 X 模组,又要和
旧模组相容,dynamic_cast 在此情况下就能派上用场(反过来说,如果没有
dynamic_cast,问题就会变得很麻烦,而且各种替代方案都不太安全);但
如果完全是可自行控制的设计,就应该尽量遵循 B2, D2, Y 的方式来组织。
以上是 dynamic_cast 的功能中,downcast 的部份。另外,由於 C++ 支援
多重继承的机制,dynamic_cast 亦可用於 crosscast(横向转型),例如:
struct Z : B1, B2 // 多重继承
{
void f();
void g();
} z;
void foo()
{
B1 *b1 = &z;
B2 *b2 = dynamic_cast<B2*>(b1); // crosscast
}
这个例子可以用来说明,dynamic_cast 实际上的动作是「型别检测」,而非
仅仅是「转型」,後者只是它输出的结果。此例中,b1 表面上是 (B1 *),但
它实际上参照的是 Z 物件,因此 dynamic_cast 侦测的结果,就是它可以顺
利转为 (B2 *) 。
: struct Base{virtual ~Base(){}};
: struct Derived: public Base{};
: Base pb = new Derived;
: Derived pd = dynamic_cast<Derived*>(pb);
: 如果 pb 真的是指向 Derived, 则 dynamic_cast<> 传回 pb 的地址
: 否的话,pd 为 NULL
: boost 有提供 polymorphic_cast<>, 用处同 dynamic_cast<>
: 差别在於错误时不是传回 NULL, 而是丢出 std::bad_cast
: polymorphic_downcast<> 则是专门用来在已知必然成功的 downcast
: 这种时候内建的 static_cast<> 和 dynamic_cast<> 其实都可以用,
: 只是 static_cast<> 没有错误检查,dynamic_cast<> 效率太差
: polymorphic_downcast<> 则使用 assert(); 来检查是否成功
: http://www.boost.org/libs/conversion/cast.htm
: : c-style cast
: 等同於上面全部...
: 在 C++ 中替他们分类,避免造成混淆
: 不过对初学者来说的话这麽多才是混淆吧,我猜 :p
(下略)
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 59.120.214.120
1F:推 godfat:感谢指导,虽然我觉得跟我讲的没啥冲突 XD 大概是太随便了 03/13 01:44
※ 编辑: cppOrz 来自: 59.120.214.120 (03/13 04:50)
※ crazying:转录至看板 NTUGIEE_EDA 03/13 11:58
2F:推 abovelight:推一个~了解更深入了 03/14 18:16