Comments (10)
我觉得采用“运行时检查”的概念就很不好,混淆了语义和实现,应该抛弃之。
dynamic_cast<T *>(v)
的良构问题,忽略 cv 限定和转换为 void *
的话:
- 如果
T
不是完整类类型或者v
的类型不是指向完整类类型的指针,则非良构; - 如果转换类不变,则良构;
- 如果从派生类到基类:
- 如果基类无歧义且可访问,则良构;
- 否则,非良构;
- 其他情况:
- 如果
v
的类型是指向多态类型的指针,则良构; - 否则,非良构。
- 如果
良构情况的语义问题(C++ 现行标准的文本写得很复杂,因为引入了“运行时检查”的概念,下面这个版本等效):
- 如果
v
是nullptr
则结果是nullptr
,否则继续; - 找出
v
的最派生对象u
; - 找出
u
的所有T
基类子对象t_1, ..., t_n
; - 若存在惟一的
1<=i<=n
使t_i
和v
有基类子对象关系(t_i
是v
的基类子对象或v
是t_i
的基类子对象或v
就是t_i
),则结果是指向t_i
的指针(这里不需要n=1
); - 否则,若
n=1
,则结果是指向t_1
的指针; - 否则,结果是
nullptr
。
C++ 标准的规定:
- 除了类不变、派生类到基类,都要求多态类型;
- 除了类不变、派生类到基类、任意类到
void *
、从nullptr
转换,都算是“运行时检查”。
这里的重点在于:
- 从多态类型出发的转换不一定有“运行时检查”,比如从多态类型到它的基类;
- “运行时检查”不一定是从基类到派生类,也可以是表面上没有继承关系的类。
因此
dynamic_cast< 新类型 >( 表达式 )
如果 表达式 是到多态类型Base
的指针或引用,且 新类型 是到Derived
类型的指针或引用,那么会进行运行时检查。
这个说法不够全面。而
如果是多态类型的话:
执行行运行时类型检查
这个说法和现行 C++ 标准不一致。
最后,从实现效率考虑,假设编译器对 v
的情况一无所知,并采用通常的实现:
- 如果类不变,则没有任何运行时开销;
- 如果是从派生类到非
virtual
基类,且基类的 offset 是 0,则没有任何运行时开销; - 如果是从派生类到非
virtual
基类,且基类的 offset 不是 0,则运行时需要判断nullptr
并条件加减数; - 如果是从派生类到
virtual
基类,则运行时需要判断nullptr
并进行某些 indirection,这个开销可能比 5 低,也可能和 5 一起处理; - 如果是其他转换,则会有较高的运行时开销。
仓库原文里
对不明确的指针的转换将失败
这句话本身就很不明确:
- 类不变、派生类到基类,和最派生对象的其他基类子对象没有关系;
- 基类到派生类,因为语义里步骤 4 的规则,如果
v
指向的对象确实是某个派生类对象的一部分,则以被转换的指针所指向的对象为基类子对象的情况优先,这时最派生对象可以含有其他T
基类子对象; - 其他情况,最派生对象必须有惟一的
T
基类子对象。
应该特别注意,从基类到派生类,有两种模式(取决于 v
具体指向最派生对象的哪个基类子对象)。
仓库原文里
可以在整个类层次结构中移动指针,包括向上转换、向下转换
不够全面——可以向上、向下、旁支转换。
还应该注意,派生类转基类,如果基类是 virtual
无歧义可访问,那么 static_cast
不可以,但 dynamic_cast
可以,此时也不需要派生类是多态类型(具有 virtual
基类并不会导致类型成为多态类型,只有 virtual
函数才会导致)。
from interview.
from interview.
from interview.
from interview.
除了类不变、派生类到基类、任意类到 void *、从 nullptr 转换,都算是“运行时检查”。
学到了🤣。
不过能详细聊一下嘛,以及
“运行时检查”不一定是从基类到派生类,也可以是表面上没有继承关系的类
能举个例子嘛?
from interview.
其实另外一部分就提到了
不够全面——可以向上、向下、旁支转换。
如下:
struct B1 { virtual f1();};
struct B2 { virtual f2();};
struct D: B1, B2 {};
void fun(B1 *p) {
auto p = dynamic_cast<B2*>(p);
}
此处从B1*
向B2*
的sidecast就是
“运行时检查”不一定是从基类到派生类,也可以是表面上没有继承关系的类
这个转换要藉由struct D
这样的旁支选择无歧义的时候才能进行
from interview.
@Mq-b 后面问题的例子:
struct B1 { virtual ~B1(); };
struct B2 { };
struct D : B1, B2 { };
D d;
B1 *b1 = &d;
// B1 和 B2 表面上没有继承关系
// b1 是 B1 * 而 B1 是多态类型
// b1 指向对象的最派生对象是 d
// d 里面的 b1 是公开基类 B1 的子对象
// d 有无歧义基类 B2 且 B2 是 D 的公开基类
// 转换得到这个 B2 基类子对象的指针
B2 *b2 = dynamic_cast<B2 *>(b1);
@dynilath 的例子没有体现 B2
不需要是多态类型,另外
这个转换要藉由
struct D
这样的旁支选择无歧义的时候才能进行
准确来说,是不考虑访问性时选择无歧义且基类关系公开,注意有惟一公开基类、不惟一基类的时候,转换失败。
第一部分的问题,单纯是 C++ 标准,见 expr.dynamic.cast,下面假设 dynamic_cast<C *>(v)
且 v
是 V *
且 C
和 V
都是完整类类型且 v
不等于 nullptr
,无关的内容都略:
- 略;
- 略;
- 如果
C
就是V
,则结果是v
; - 如果
C = B
是V = D
的基类,那么结果是v
的惟一B
基类子对象;如果D
的基类B
歧义或者不可访问,则程序非良构; - 否则,
V
必须是多态类型; - 略(从
nullptr
转换); - 略(转换到
void *
); - 进行“运行时检查”,设
v
指向对象的最派生对象是u
:
a. 如果v
指向u
的某个C
基类子对象c
的某个公开基类子对象(用v <= c <= u
表示),且不存在不是c
的c'
使v <= c' <= u
且c'
是C
,则结果是指向c
的指针;
b. 如果v
指向u
的某个公开基类子对象,且u
具有惟一的C
基类子对象c
,且C
是公开基类,则结果是指向c
的指针; - 略。
这里比较变态的点是关于 public
和可访问性的细节,我的“等效”表述里面忘记考虑。But still, 这个标准相当难读,而且有不少“陷阱”。
上面的叙述里“运行时检查”排除了 2、3、6、7。
from interview.
考虑到“运行时检查”出现在标准文本中,我们是不是该考虑下提个编辑 issue?@GeeLaw
from interview.
我懒,而且这只是不幸的选词,而不是技术问题。
from interview.
而且这只是不幸的选词,而不是技术问题。
……正因此我建议通过 editorial issue 处理,如果是技术问题反而不该这么做。
from interview.
Related Issues (20)
- C/C++ 智能指针中,unique_ptr小节有错别字(粗体处) HOT 3
- 图挂了
- dynamic_cast,向下转型返回空指针才对
- 大佬,图片挂了
- SMTP的全称似乎错了
- 无名管道,应为单工通信,写的半双工 HOT 2
- End HOT 5
- 大佬,我基于你的这个文档做了一个小程序,可否贴在这里呢? HOT 4
- CPP动态多态处勘误 HOT 3
- 单词里少了个字母 HOT 2
- 快排算法的疑问 HOT 3
- c++
- 快排里的else left++会执行吗?
- Interview HOT 3
- C++ 的inline 远不止如此 HOT 3
- #pragma pack(n) 应该强调是编译器扩展 HOT 2
- 使用typedef struct在C++毫无意义
- 如何定义一个只能在栈上生成对象的类 HOT 3
- 关于 `extern “C”` 的描述,是不严谨,乃至错误的。 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from interview.