我在运行此程序时遇到“分段错误”。请区分以下两个程序
class xxx{
public: virtual void f(){cout<<"f in xxx"<<endl;} //virtual function
virtual void g(){cout<<"g in xxx"<<endl;} //virtual function
};
class yyy{ //yyy has no relation with xxx at this context
public: virtual void f(){cout<<"f in yyy"<<endl;} //virtual function but no relation with xxx class
void g(){cout<<"g in yyy"<<endl;}
};
int main(int argc, char *argv[])
{
xxx x1,*x;
yyy y1;
x=&x1;
x->f();
x->g();
x=(xxx*) &y1; //one class pointer containing another class object address
x->f();
x->g();
}
- 输出
f in xxx
g in xxx
f in yyy
Segmentation fault
但是根据多态性概念具有相同的问题
class xxx{
public: virtual void f(){cout<<"f in xxx"<<endl;} //virtual function
virtual void g(){cout<<"g in xxx"<<endl;} //virtual function
};
class yyy:public xxx{ //yyy is derived from xxx
public: virtual void f(){cout<<"f in yyy"<<endl;}
void g(){cout<<"g in yyy"<<endl;}
};
int main(int argc, char *argv[])
{
xxx x1,*x;
yyy y1;
x=&x1;
x->f();
x->g();
x=(xxx*) &y1; //parent class pointer having derived class address
x->f();
x->g();
}
- 输出
f in xxx
g in xxx
f in yyy
g in yyy
答案 0 :(得分:2)
在第一种情况下,强制转换会产生垃圾,因为你告诉编译器如果&y1
指向xxx
则不会。在第二种情况下,yyy
是xxx
,因此将指向yyy
的指针转换为指向xxx
的指针是安全的。因为它是一个。
如果您有指向卡车的指针,您可以将其视为指向车辆的指针,因为卡车是车辆。但是如果你有一个指向花的指针并将其视为指向车辆的指针,它本质上是一个垃圾指针,并且取消引用它是未定义的行为。
您应该使用C ++样式转换而不是C样式转换。如果你被迫表达预期的语义,那么抓住这种错误的可能性会更大。 (编写第一个代码的程序员是否认为它可以作为static_cast
工作?或者他们认为它可以作为reinterpret_cast
工作吗?额外的想法选择一个,或者在没有演员的情况下执行操作,可能已经避免了错误。)
答案 1 :(得分:0)
在你的第一个例子中:
x=(xxx*) &y1; //one class pointer containing another class object address
x->f();
x->g();
变量x
实际上指向与类yyy
无关的类xxx
。 f()
在两者中都被声明并且是唯一的虚函数的事实使它恰好工作。由于g()
是类xxx
中的虚函数,而不是yyy
中的虚函数,因此编译器将生成代码来调用虚函数g()
,其中包含一些垃圾。虚拟函数表中的f()
之后发生的任何事情(又名vtable
)[实际上虚函数的实现方式当然是一个实现细节,但是可以预期存在某种“表“存储在每个类的某个地方的函数”。
如果yyy
被声明为这样,那么它会给你更有趣的结果:
class yyy{ //yyy has no relation with xxx at this context
public: virtual void g(){cout<<"g in yyy"<<endl;} //virtual function but no relation with xxx class
void f(){cout<<"f in yyy"<<endl;}
};
它会告诉你,由于调用f()
而导致“g yyy”,然后在调用g()
时失败... ...假设虚函数工作的方式不是根据他们的名字调用它们,但是它们在类中的“顺序”[再次,确切地说它是如何工作的是一个实现细节,并且由于我们正在处理“未定义的行为”,编译器也被允许打印屏幕上闪烁的红色文字中的“答案是42”,或者在紧急呼叫时听起来像救护车 - 但这比我在典型的C ++编译器实现中所描述的更不可能发生。
在第二种情况下,由于virtual
的{{1}}被继承到xxx
,因此yyy
中的函数g()
也是虚拟的,因为“它的工作方式与你期望的一样“,因为你所做的正是你应该做的。
另请注意,有一些C ++样式转换可以帮助您避免出现这些错误 - 如果您做错了,它会在编译时发出错误消息(yyy
),或者使用{{1在编译器无法在编译时确定类型的地方,它会给出static_cast
作为结果,这是一个明显的指示,表明你弄错了。