C++ 为什么';没有指针/引用,多态性不能工作吗?
我确实发现了一些标题类似的问题,但当我阅读答案时,他们关注的是问题中真正具体的不同部分(例如STL/容器) 有人能告诉我为什么必须使用指针/引用来实现多态性吗?我可以理解指针可能会有所帮助,但引用肯定只区分按值传递和按引用传递C++ 为什么';没有指针/引用,多态性不能工作吗?,c++,pointers,polymorphism,virtual,C++,Pointers,Polymorphism,Virtual,我确实发现了一些标题类似的问题,但当我阅读答案时,他们关注的是问题中真正具体的不同部分(例如STL/容器) 有人能告诉我为什么必须使用指针/引用来实现多态性吗?我可以理解指针可能会有所帮助,但引用肯定只区分按值传递和按引用传递 当然,只要您在堆上分配内存(这样您就可以进行动态绑定),那么这就足够了。显然不行。当对象按值传递时,它通常被放在堆栈上。把东西放在堆栈上需要知道它有多大。当使用多态性时,您知道传入对象实现了一组特定的功能,但是您通常不知道对象的大小(您也不应该知道,这是好处的一部分)。因
当然,只要您在堆上分配内存(这样您就可以进行动态绑定),那么这就足够了。显然不行。当对象按值传递时,它通常被放在堆栈上。把东西放在堆栈上需要知道它有多大。当使用多态性时,您知道传入对象实现了一组特定的功能,但是您通常不知道对象的大小(您也不应该知道,这是好处的一部分)。因此,不能将其放在堆栈上。但是,您总是知道指针的大小 现在,并不是所有的事情都会发生,还有其他可以减轻罪责的情况。对于虚拟方法,指向对象的指针也是指向对象的vtable的指针,vtable指示方法所在的位置。这允许编译器查找和调用函数,而不管它使用的是什么对象 另一个原因是,对象通常在调用库之外实现,并使用完全不同(可能不兼容)的内存管理器进行分配。它还可能包含无法复制的成员,或者如果使用其他管理器复制,则会导致问题。复制可能会有副作用和其他各种并发症 结果是指针是对象上唯一您真正正确理解的信息位,并提供足够的信息来确定您需要的其他位的位置 “当然,只要在堆上分配内存”——分配内存的地方与此无关。这都是关于语义的。例如:
Derived d;
Base* b = &d;
d
在堆栈上(自动内存),但多态性仍将在b
上工作
如果您没有基类指针或对派生类的引用,多态性将不起作用,因为您不再有派生类。接受
Base c = Derived();
c
对象不是派生的
,而是基
,因为切片。所以,从技术上讲,多态性仍然有效,只是你不再有一个派生的
对象可以谈论
现在开始
Base* c = new Derived();
c
只是指向内存中的某个位置,您并不真正关心它实际上是基
还是派生的
,但是对虚拟
方法的调用将被动态解析。您需要指针或引用,因为对于您感兴趣的多态性(*)类型,您需要动态类型可以不同于静态类型,换句话说,对象的真实类型不同于声明的类型。在C++中,只有指针或引用发生。
(*)泛型,模板提供的多态性类型不需要指针和引用。C++中的
< P>,对象总是具有固定类型和大小,在编译时已知(如果它能够并且确实有地址)总是在固定地址存在,在其生存期内。这些是从C语言继承的特性,有助于使这两种语言都适合于低级系统编程。(不过,所有这些都要遵守“似乎”规则:一致性编译器可以随心所欲地使用代码,只要能够证明它对标准所保证的一致性程序的任何行为没有可检测的影响。) <> > C++中的代码>虚拟函数,定义为(或多或少,不需要极端的语言法律化),基于对象的运行时类型执行;当直接在对象上调用时,这将始终是对象的编译时类型,因此以这种方式调用virtual
函数时不存在多态性
注意,这不一定是这样的:对象类型为
因此,C++中的多态性是通过允许引用和指针指向对象来引用和指向它们声明的编译时类型的对象及其任何子类型来实现的。当通过引用或指针调用
virtual
函数时,编译器无法证明引用或指向的对象是该virtual
函数的特定已知实现的运行时类型,编译器插入查找正确的virtual
函数以调用运行时的代码。也不一定要这样:引用和poi
class Base { };
class Derived : public Base { };
Derived x; /* Derived type object created */
Base y = x; /* Copy is made (using Base's copy constructor), so y really is of type Base. Copy can cause "slicing" btw. */
class A { int x; };
A fn(A a)
{
return a;
}
class B : public A {
uint64_t a, b, c;
B(int x_, uint64_t a_, uint64_t b_, uint64_t c_)
: A(x_), a(a_), b(b_), c(c_) {}
};
B b1 { 10, 1, 2, 3 };
B b2 = fn(b1);
// b2.x == 10, but a, b and c?
std::vector<A*> vec;
template<class T>
struct PolymorphicObject {
T::vtable* __vtptr;
T __instance;
};
struct A { virtual const char* fn() { return "A"; } };
struct B : public A { virtual const char* fn() { return "B"; } };
#include <iostream>
#include <cstring>
int main()
{
A* a = new A();
B* b = new B();
memcpy(a, b, sizeof(A));
std::cout << "sizeof A = " << sizeof(A)
<< " a->fn(): " << a->fn() << '\n';
}
sizeof A = 4 a->fn(): B
struct A { int i; A(int i_) : i(i_) {} virtual const char* fn() { return "A"; } };
struct B : public A {
int j;
B(int i_) : A(i_), j(i_ + 10) {}
virtual const char* fn() { return "B"; }
};
#include <iostream>
#include <cstring>
int main()
{
A* a = new A(1);
B* b = new B(2);
*a = *b; // aka a->operator=(static_cast<A*>(*b));
std::cout << "sizeof A = " << sizeof(A)
<< ", a->i = " << a->i << ", a->fn(): " << a->fn() << '\n';
}