我在下面发现 没有指针的 C++ 多态性 ,它解释了具有多态性功能 C++ 必须使用指针或引用类型。
我查看了一些进一步的资源,所有这些资源都说相同,但原因。
是否有任何技术困难来支持具有值的多态性,或者有可能但 C++ 决定不提供这种能力?
我在下面发现 没有指针的 C++ 多态性 ,它解释了具有多态性功能 C++ 必须使用指针或引用类型。
我查看了一些进一步的资源,所有这些资源都说相同,但原因。
是否有任何技术困难来支持具有值的多态性,或者有可能但 C++ 决定不提供这种能力?
多态处理值的问题归结为对象切片问题:由于派生对象可能比其基类使用更多内存,因此在自动存储(即堆栈上)中声明一个值会导致仅为基类分配内存,而不是为派生对象。因此,属于派生类的部分对象可能会被切掉。这就是为什么 C++ 设计者有意识地决定将虚拟成员函数重新路由到基类中的实现,它不能触及派生类的数据成员。
困难来自这样一个事实,即您所说的对象是在自动内存中(在堆栈上)分配的,并且必须在编译时知道其大小。
无论指针指向什么,指针的大小在编译时都是已知的,并且引用在底层实现为指针,所以不用担心。
考虑对象:
BaseObject obj = ObjectFactory::createDerived();
obj
如果createDerived()
有条件地返回派生对象,应该分配多少内存?为了克服这个问题,返回的对象被切片并“转换*为一个BaseObject
大小已知的对象。
这一切都源于“为所用付费”的心态。
The short answer is because the standard specifies it. But are there any insurmountable technical barriers to allowing it?
C++ data structures have known size. Polymorphism typically requires that the data structures can vary in size. In general, you cannot store a different (larger) type within the storage of a smaller type, so storing a child class with extra variables (or other reasons to be larger) within storage for a parent class is not generally possible.
Now, we can get around this. We can create a buffer larger than what is required to store the parent class, and construct child classes within that buffer: but in this case, exposure to said instance will be via references, and you will carefully wrap the class.
This is similar to the technique known as "small object optimization" used by boost::any
, boost::variant
and many implementations of std::string
, where we store (by value) objects in a buffer within a class and manage their lifetime manually.
There is also an issue where Derived
pointers to an instance can have different values than Base
pointers to an instance: value instances of objects in C++ are presumed to exist where the storage for the instance starts by most implementations.
So in theory, C++ could allow polymorphic instances if we restricted it to derived classes that could be stored in the same memory footprint, with the same "pointer to" value for both Derived
and Base
, but this would be an extremely narrow corner case, and could reduce the kinds of optimizations and assumptions compilers could make about value instances of a class in nearly every case! (Right now, the compiler can assume that value instances of a class C
have virtual
methods that are not overridden elsewhere, as an example) That is a non trivial cost for an extremely marginal benefit.
What more, we are capable of using the C++ language to emulate this corner case using existing language features (placement new, references, and manual destruction) if we really need it, without imposing that above cost.
目前尚不清楚您所说的“具有值的多态性”是什么意思。在 C++ 中,当你有一个类型的对象时A
,它总是表现为一个类型的对象A
。这是完全正常和合乎逻辑的事情。我看不出它如何以任何其他方式表现。因此,尚不清楚某人决定“不提供”您在谈论什么“能力”。
C++ 中的多态性意味着一件事:通过具有多态类型的表达式进行的虚函数调用是根据该表达式的动态类型解析的(与非虚函数的静态类型相反)。这里的所有都是它的。
C++ 中的多态性总是按照上述规则工作。它通过指针以这种方式工作。它通过引用以这种方式工作。它通过直接对象(您称它们为“值”)以这种方式工作。因此,说 C++ 中的多态性仅适用于指针和引用是不正确的。它也适用于“价值观”。如上所述,它们都遵循相同的规则。
但是,对于直接对象(“值”),其动态类型始终与其静态类型相同。因此,即使多态适用于直接值,它也不能证明任何真正的“多态”。具有多态性的直接对象的行为与没有多态性的直接对象的行为相同。因此,直接对象的多态性是退化的、平凡的多态性。它仅在概念上存在。这又是完全合乎逻辑的:一个类型的对象A
应该表现得像一个类型的对象A
。它还能如何表现?
为了观察实际的非退化多态性,需要一个静态类型不同于其动态类型的表达式。A
当静态类型的表达式(关于虚函数调用)表现为不同类型的对象时,会观察到非平凡的多态性B
。为此,静态类型的表达式A
必须实际引用 type 的对象B
。这仅适用于指针或引用。在表达式的静态和动态类型之间创建差异的唯一方法是使用指针或引用。
换句话说,说 C++ 中的多态性仅通过指针或引用起作用是不正确的。正确的说法是,使用指针或引用,多态性变得可观察且不平凡。