用C++切片我错了?

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对象).

另外,如果演员表演取得成功而不是"演员之后的名字:孩子",为什么我得到"演员之后的名字:父母"?

Kon*_*lph 8

你的结果是运气不好.这是我得到的结果:

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,所以任何事情都可能发生.你是"幸运的",代码只是抓住了父类的虚方法表指针.使用我的编译器,该访问显然失败了.