nap*_*ets 4 c++ inheritance object-slicing
我读到了C++中的切片问题,我尝试了一些例子(我来自Java背景).不幸的是,我不明白一些行为.目前,我被困在这个例子中(来自Efficent C++第三版的替代例子).任何人都可以帮我理解吗?
我父母的简单类:
class Parent
{
public:
Parent(int type) { _type = type; }
virtual std::string getName() { return "Parent"; }
int getType() { return _type; }
private:
int _type;
};
Run Code Online (Sandbox Code Playgroud)
我简单的一个孩子:
class Child : public Parent
{
public:
Child(void) : Parent(2) {};
virtual std::string getName() { return "Child"; }
std::string extraString() { return "Child extra string"; }
};
Run Code Online (Sandbox Code Playgroud)
主要的:
void printNames(Parent p)
{
std::cout << "Name: " << p.getName() << std::endl;
if (p.getType() == 2)
{
Child & c = static_cast<Child&>(p);
std::cout << "Extra: " << c.extraString() << std::endl;
std::cout << "Name after cast: " << c.getName() << std::endl;
}
}
int main()
{
Parent p(1);
Child c;
printNames(p);
printNames(c);
}
Run Code Online (Sandbox Code Playgroud)
我得到执行后:
姓名:家长
姓名:家长
额外:儿童额外的字符串
演员后的姓名:家长
我理解前两行,因为它是"切片"的原因.但我不明白为什么我可以通过静态演员将孩子投射到父母身上.在书中,有人写道,在切片之后,所有专业信息都将被切掉.所以我想,我不能将p转换为c,因为我没有信息(在函数printNames的开头,创建了一个没有附加信息的新Parent对象).
另外,如果演员表演取得成功而不是"演员之后的名字:孩子",为什么我得到"演员之后的名字:父母"?
你的结果是运气不好.这是我得到的结果:
Name: Parent
Name: Parent
Extra: Child extra string
bash: line 8: 6391 Segmentation fault (core dumped) ./a.out
Run Code Online (Sandbox Code Playgroud)
以下是您的代码中发生的情况:
传递c到时printNames,会发生转换.特别是,由于传递是按值进行的,因此您将调用Parent隐式声明的复制构造函数,其代码如下所示:
Parent(Parent const& other) : _type{other._type} {}
Run Code Online (Sandbox Code Playgroud)
换句话说,你复制_type变量c 和其他任何东西.你现在有了一个新的类型对象Parent(它的静态类型和动态类型都是Parent),c实际上并没有传递给它printNames.
在函数内部,然后强制转换p为a Child&.这个演员阵容不能成功,因为p它不是一个Child,或者可以转换成一个,但是C++没有给你任何诊断(这实际上是一种耻辱,因为编译器可以简单地证明演员是错误的).
现在我们处于未定义行为的领域,现在一切都被允许发生.实际上,由于Child::extraString从不访问this(隐式或显式),对该函数的调用才成功.调用是在非法对象上完成的,但由于该对象从未被触及,因此可行(但仍然是非法的!).
下一个调用to Child::getName是一个虚拟调用,因此需要显式访问this(在大多数实现中,它访问虚方法表指针).而且,因为代码是UB,所以任何事情都可能发生.你是"幸运的",代码只是抓住了父类的虚方法表指针.使用我的编译器,该访问显然失败了.