为什么C++需要'对象切片'?为什么允许这样做?更多错误?

Gob*_*0st 11 c++ standards slice

为什么C++标准允许对象切片?

请不要向我解释c ++对象切片概念,因为我知道这一点.

我只是想知道这个c ++功能(对象切片)设计背后的意图是什么?

为了获得新手更多的错误?

对于c ++来说,防止对象切片不是更安全吗?

以下是标准和基本切片示例:

class Base{
public:
       virtual void message()
       {
               MSG("Base ");
       }
private:
    int m_base;
};

class Derived : public Base{
public:
       void message()
       {
               MSG("Derived "); 
       }
private:
       int m_derive;
};

int main (void)
{
    Derived dObj;

    //dObj get the WELL KNOWN c++ slicing below
    //evilDerivedOjb is just a Base object that cannot access m_derive
    Base evilDerivedOjb = dObj;  //evilDerivedObj is type Base
    evilDerivedOjb.message();    //print "Baes" here of course just as c++ standard says
}
Run Code Online (Sandbox Code Playgroud)

提前致谢.

================================================== ===============================阅读完所有答案和评论后,我想我应该首先更好地表达我的问题但它来了:

如果存在is-a关系(公共继承),而不是私有/受保护继承,则可以执行以下操作:

class Base{
public:
    virtual void foo(){MSG("Base::foo");}
};

class Derived : public Base{
public:
    virtual void foo(){MSG("Derived::foo");}
};

int main (void)
{
    Base b;
    Derived d;
    b = d;                      //1
    Base * pB = new Derived();  //2
    Base& rB = d;               //3

    b.foo();    //Base::foo
    pB->foo();  //Derived::foo
    rB.foo();   //Derived::foo
}
Run Code Online (Sandbox Code Playgroud)

众所周知,只有2和3多态地工作,而其中一个是臭名昭着的对象切片,除了一个bug之外什么都不会产生!

注1,2和3 NEED是一种工作关系.
如果您使用私有/保护继承,您将收到所有这些的编译错误:

'type cast' : conversion from 'Derived *' to 'const Base &' exists, but is inaccessible
'type cast' : conversion from 'Derived *' to 'Base *' exists, but is inaccessible
'type cast' : conversion from 'Derived *' to 'Base &' exists, but is inaccessible
Run Code Online (Sandbox Code Playgroud)

所以我的问题(初衷)是问如果c ++标准使1编译错误同时保持允许2和3会更好吗?

希望这次我更好地表达了我的问题.

谢谢

Lig*_*ica 10

我想你正在向后看.

没有人坐下来说"好吧,我们需要用这种语言切片." 切片本身不是语言特征; 它是当你打算以多态方式使用对象但却出错并复制它们时发生的事情的名称.您可能会说它是程序员错误的名称.

可以"静态"复制这些对象是C++和C的基本特性,否则你将无法做很多事情.

编辑:[由杰里科芬(希望托马拉克会原谅我的劫持他的答案)].我添加的大部分内容都是相同的,但更直接来自源代码.一个例外(正如你所看到的),奇怪的是,有人确实说过"我们需要用这种语言切片".Bjarne谈到了在C++的设计和演化(第11.4.4节)中的切片.除此之外,他说:

我从实际角度对切片持谨慎态度,但除了添加一个非常特殊的规则外,我没有看到任何阻止它的方法.此外,当时,我从Ravi Sethi那里得到了一个独立的请求,要求他从理论和教学的角度来看这个"切片语义":除非你能将派生类的对象分配给它的公共基础的对象class,那么这将是C++中唯一不能使用派生对象代替基础对象的点.

我注意到Ravi Sethi是龙书的作者之一(在许多其他事情中),所以无论你是否同意他,我认为很容易理解他对语言设计的看法会带来多少重量.


Naw*_*waz 8

由于is-a关系,这是允许的.

当你公开1派生DerivedBase,你正在宣布编译器Derived 是一个 Base.因此应该允许这样做:

Base base = derived;
Run Code Online (Sandbox Code Playgroud)

然后base按原样使用.那是:

base.message(); //calls Base::message();
Run Code Online (Sandbox Code Playgroud)

读这个:

如果你是私下派生DerivedBase,那就是has-a关系.那是一种构成.阅读本文此内容.

但是,在您的情况下,如果您不想切片,那么您可以这样做:

Base & base = derived;
base.message(); //calls Derived::message();
Run Code Online (Sandbox Code Playgroud)

来自你的评论:

如果只允许指针/引用为is-a relationshp工作,那么C++防止对象切片会不会更好?

is-a如果基础具有虚函数,则指针和引用不保持关系.

 Base *pBase = &derived;
 pBase->message(); //doesn't call Base::message(). 
 //that indicates, pBase is not a pointer to object of Base type.
Run Code Online (Sandbox Code Playgroud)

当你想要一种类型的一个对象表现得像它的基类型的对象时,那就是所谓的is-a关系.如果使用指针或基类型的引用,则它不会调用Base::message(),表示指针或引用不具有指针或对类型对象的引用.


Mar*_*k B 5

你会如何防止语言中的对象切片?如果一个函数在堆栈上需要16个字节(例如作为参数)并且你传递一个更大的对象,比如24个字节,那么地球上的被调用者会知道该怎么做?C++不像Java那样一切都是引用的引用.简短的回答是,假设C++(如C)允许对象的值和引用语义,那么就没有办法避免对象切片.

编辑:有时你不关心对象切片和彻底禁止它可能会阻止一个有用的功能.