为什么是';对象切片';C+中需要+?为什么允许这样做?为了更多的虫子? 为什么C++标准允许对象切片?
请不要向我解释C++对象切片概念,我知道。 我只是想知道这个C++特性(对象切片)设计背后的意图是什么?p> 让新手有更多的bug < C++ >防止对象切片的类型安全性不高吗?< /P> 下面是一个标准和基本的切片示例:为什么是';对象切片';C+中需要+?为什么允许这样做?为了更多的虫子? 为什么C++标准允许对象切片?,c++,standards,slice,C++,Standards,Slice,请不要向我解释C++对象切片概念,我知道。 我只是想知道这个C++特性(对象切片)设计背后的意图是什么?p> 让新手有更多的bug < C++ >防止对象切片的类型安全性不高吗?< /P> 下面是一个标准和基本的切片示例: class Base{ public: virtual void message() { MSG("Base "); } private: int m_base; }; class Derive
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
}
提前谢谢
=================================================================================
在阅读了所有的答案和评论后,我认为我首先应该更好地表达我的问题,但问题来了:
当存在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
}
众所周知,只有2和3可以多态工作,而其中一个是臭名昭著的对象
切片只会产生一个bug
注1、2和3需要是一种工作关系。如果您使用的是私有/保护继承,则所有这些继承都会出现编译错误:
'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
所以我的问题(初衷)是问如果C++标准会更好吗?
使1成为编译错误,同时继续允许2和3
希望这次我能更好地表达我的问题
谢谢我想你是在向后看 没有人坐下来说“好吧,我们需要用这种语言进行切片”;这是当您打算以多态方式使用对象,但却出错并复制它们时发生的情况的名称。你可能会说这是一个程序员bug的名字 <> >“静态”复制对象是C++和C的基本特征,否则将不能做很多事情。 编辑:[杰里·科芬(希望托马拉克能原谅我劫持了他的答案)]。我所添加的大部分内容都是沿着相同的思路,但更直接地来自源代码。一个例外(如你所见),奇怪的是,有人确实说“我们需要用这种语言来切片”。他还说: 从实用的角度来看,我对切片持谨慎态度,但除了添加一条非常特殊的规则外,我看不到任何防止这种情况发生的方法。另外,当时,我有一个独立的请求,就是Ravi Sethi提出的这些“切片语义”,他从理论和教学的角度想要它:除非你能将派生类的对象分配给它的公共基类的对象,然后,C++中唯一的一点就是不能用派生对象代替基对象。
我要指出的是,拉维·塞蒂是《龙之书》的作者之一(除其他外),因此,无论你是否同意他的观点,我认为很容易理解他关于语言设计的观点在哪里具有相当大的分量。这是允许的,因为
是-a
关系
当您从Base
公开1派生Derived
时,您向编译器说明Derived
是一个Base
。因此,应允许其这样做:
Base base = derived;
然后按原样使用base
。即:
base.message(); //calls Base::message();
请阅读以下内容:
Base
派生Derived
,则它是具有-a
关系。那是一种构图。读和读
但是,在您的情况下,如果不需要切片,则可以执行以下操作:
Base & base = derived;
base.message(); //calls Derived::message();
根据您的评论:
对于C++来说,防止对象切片不是更好,而只允许指针/引用为IS-A关系?p> 否。如果基础具有虚拟功能,则指针和引用不会保持
is-a
关系
当您希望一种类型的一个对象的行为类似于其基本类型的对象时,这称为is-a
关系。如果使用基类型的指针或引用,则它不会调用base::message()
,这表示指针或引用没有类似于基类型对象的指针或引用。如何防止在该语言中进行对象切片?如果一个函数希望堆栈上有16个字节(例如作为一个参数),而你传递了一个更大的对象,比如说24个字节,那么被调用方到底怎么知道该怎么做呢?C++不像java那样,所有的东西都是引擎盖下的引用。简短的答案是,没有任何方法可以避免对象切片,假设C++,比如C,允许对象的值和引用语义。
编辑:有时你不在乎对象是否被切分并直接禁止它可能会阻止一个有用的功能。基本对象对
m_base
有什么访问权限
不能执行
baseObj.m_base=x代码>它是一个私人成员。你只能使用基类的公共方法,所以创建一个基本对象并没有多大区别。 对象切片是继承和替代性的自然结果,它不限于C++,并且它不是故意引入的。接受基的方法只能看到基中存在的变量。复制构造函数和赋值运算符也是如此。但是,它们通过复制此切片对象(可能有效,也可能无效)来传播问题
当您将多态对象视为值类型时,通常会出现这种情况,在过程中涉及复制构造函数或赋值运算符,这通常是由编译器生成的。在处理多态对象时,请始终使用引用或指针(或指针包装器),切勿在游戏中混用值语义。如果需要多态对象的副本,请改用动态clone
方法
一半的解决方案是在赋值时检查源对象和目标对象的typeid
Base *pBase = &derived;
pBase->message(); //doesn't call Base::message().
//that indicates, pBase is not a pointer to object of Base type.