在以下代码中:
#include <iostream>
#include <thread>
#include <mutex>
#include <functional>
struct IView {
virtual void setOnClick() = 0;
};
struct ITextView : IView {
virtual void setText() = 0;
};
struct IButton : ITextView {
virtual void setRadius() = 0;
};
struct View : IView {
int i = 1;
virtual void setOnClick() override {
std::cout << "setting OnClick! i: " << i << std::endl;
};
};
/// Works as is
/// But if make "TextView : View, ITextView" - have segfault on the run
struct TextView : ITextView, View {
int j = 2;
virtual void setText() override {
std::cout << "setting text! i: " << i << " j: " << j << std::endl;
};
// forward IView
virtual void setOnClick() override {
View::setOnClick();
}
};
int main() {
TextView tv;
void* ptr = &tv; // I need to pass raw pointer, and then restore "interface" from it
ITextView* itv = static_cast<ITextView*>(ptr); // I don't need safety checks here
itv->setOnClick();
itv->setText();
return 0;
}
如果我更改了TextView
的继承顺序,我会在itv->setText();
来电时遇到段错误。
为什么重要?我可以在这里使用static_cast
,或者我在这里使用UB?据我所知dynamic_cast
只需要虚拟继承,而且正如我所知道的那样,不是那种情况。
答案 0 :(得分:5)
目前,您隐式地从TextView*
转换为void*
,然后明确地从void*
转换为ITextView*
。从/向void*
进行转换时,这些转换不会执行任何指针调整,因此最终会得到ITextView*
类型的指针,该指针实际上指向TextView
(而不是ITextView
subobject!):未定义的行为随之而来。
解决方案是在void*
的两个侧面使用完全相同的类型来处理始终:
TextView tv;
void* ptr = static_cast<ITextView*>(&tv); // Adjust, then convert to void*
ITextView* itv = static_cast<ITextView*>(ptr);
答案 1 :(得分:4)
指向不同类别“切片”的指针是不同的。
所以当你通过void *传输时,你需要
void * ptr = static_cast<ITextView *>(&tv);
...
ITextView* itv = static_cast<ITextView*>(ptr)
请注意,您的基类层次结构中有两个IView“实例”:
TextView <- ITextView <- ... <- IView
\- View <- IView
有虚拟继承将其转换为“钻石”:wikipedia
答案 2 :(得分:0)
关键是你正在对static_cast
传递一个void*
来传递一个TextView tv;
ITextView* itv = static_cast<ITextView*>(&tv);
,它将实际的类型隐藏到编译器中。
我的意思是:
TextView tv;
void* ptr = &tv;
ITextView* itv = static_cast<ITextView*>(ptr);
这是安全的,您显式转换为父类型。
ptr
这里编译器不知道ITextView*
的实际类型并将其视为ITextView
,因此它无法调整指向对象的正确子部分的指针(它是值得注意的是,这似乎有效,因为---------------------
| VTable A | Type A |
---------------------
---------------------
| VTable B | Type B |
---------------------
-----------------------------------------
| VTable AB | Type A | Type B | Type AB |
-----------------------------------------
没有任何成员变量)。这与如何实现继承有关,请考虑:
class A { /* members */ };
class B { /* members */ };
class AB : public A, public B { /* members */ };
假设这是
的内存布局AB
现在很明显,如果你有B*
的地址并且你把它转换为AB
而没有让编译器知道它是AB
那么编译器不是能够指向正确的 B型部分,它位于 A型切片之后。只有知道首先是 qry1=spark.sql("SELECT * FROM (SELECT col1 as clf1, col2, count(col2) AS value_count FROM table1 GROUP BY col2,col1 order by value_count desc) a where value_count !=1")
才能知道这一点。