当前位置: 首页 > 知识库问答 >
问题:

为什么没有指针/引用多态性就不能工作?

金嘉言
2023-03-14

我确实发现了一些标题类似的问题,但当我阅读答案时,他们关注的是问题中真正具体的不同部分(例如STL/容器)。。

有人能告诉我为什么必须使用指针/引用来实现多态性吗?我能理解指针可能会有所帮助,但引用肯定只区分按值传递和按引用传递??

当然,只要在堆上分配内存——这样就可以进行动态绑定,那么这就足够了——显然不行。

共有3个答案

左丘昊天
2023-03-14

我发现理解像这样分配时调用复制构造函数真的很有帮助:

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. */ 

由于y是Base类的实际对象,而不是原始对象,因此在此类上调用的函数是Base的函数。

杨宏儒
2023-03-14

在C语言中,一个对象在编译时总是有一个固定的类型和大小,并且(如果它可以并且确实有自己的地址)在其生命周期内总是存在于一个固定的地址。这些都是从C语言继承来的特性,有助于使这两种语言都适合于低级系统编程。(不过,所有这一切都要遵守“仿佛”规则:只要能够证明它对标准保证的一致性程序的任何行为没有可检测的影响,一致性编译器就可以随意使用代码。)

C中的virtual函数定义为基于对象的运行时类型执行(或多或少,不需要极端的语言律师);当直接在对象上调用时,这将始终是对象的编译时类型,因此当以这种方式调用virtual函数时,不存在多态性。

请注意,这并不一定是这样的:具有虚拟函数的对象类型通常用C实现,每个对象都有一个指向虚拟函数表的指针,这对每种类型都是唯一的。如果有这种倾向,一些假设的C变体的编译器可以实现对对象(如Base b; b=Derive())的赋值,即复制对象的内容和虚拟表指针,如果BaseDerive大小相同,这将很容易工作。在两者大小不同的情况下,编译器甚至可以插入代码,使程序暂停任意时间,以便重新排列程序中的内存,并以一种可能被证明对程序的语义学没有可检测到的影响的方式更新对该内存的所有可能的引用,如果找不到这种重新排列,就终止程序:这将是非常低效的,但是,不能保证永远停止,显然不是赋值操作符所希望的功能。

因此,C中的多态性是通过允许对象的引用和指针引用并指向其声明的编译时类型及其任何子类型的对象来实现的。当通过引用或指针调用virtual函数时,编译器无法证明引用或指向的对象是运行时类型,并且该virtual函数的特定已知实现,编译器插入查找正确的virtual函数以调用运行时的代码。也不必如此:引用和指针可以被定义为非多态性(不允许它们引用或指向其声明类型的子类型),并迫使程序员想出实现多态性的替代方法。后者显然是可能的,因为它一直都是用C语言完成的,但在那一点上,没有太多理由有一种新的语言。

总之,C的语义设计允许对面向对象的多态性进行高级抽象和封装,同时仍然保留了一些特性(如低级访问和内存的显式管理),这些特性允许C适合于低级开发。您可以很容易地设计一种具有其他语义的语言,但它不是C语言,并且会有不同的优点和缺点。

史承福
2023-03-14

“当然,只要你在堆中分配内存”——内存在哪里分配与此无关。这都是关于语义学的。举个例子:

Derived d;
Base* b = &d;

d在堆栈上(自动内存),但多态性仍将在b上工作。

如果您没有基类指针或派生类的引用,多态将不起作用,因为您不再有派生类

Base c = Derived();

由于切片,c对象不是衍生,而是Base。所以,从技术上讲,多态仍然有效,只是您不再有衍生对象可以讨论。

现在拿

Base* c = new Derived();

c只是指向内存中的某个位置,您并不真正关心这实际上是Base还是Derive,但是对虚拟方法的调用将被动态解析。

 类似资料:
  • 我来这里是为了消除专家们对opengl的一些困惑。我感谢你的帮助! 顶点着色器代码看起来像 所以,这是我的理解。GLVertexAttributePointer的目的是定义顶点缓冲区对象中的数据格式。因此,在vbo中,它按如下方式存储数据 所以,我们有两条glVertex AttribPointer线,因为我们在顶点着色器中定义了两个变量。所以基本上我们定义了这两个变量指向什么。因此,第一个glV

  • 问题内容: 这个来自json.Unmarshal docs的示例(为便于使用而不是进行了稍微修改)有效,没有错误: 工作示例的游乐场链接 但是这个经过稍微修改的示例却没有: 非工作示例的游乐场链接 它显示了这个实际上并没有帮助的模糊错误(看起来更像是一个函数调用,而不是错误的IMO): json:Unmarshal(nil * main.Animal) 这似乎是因为它是未初始化的指针。但是文档说(

  • 本文向大家介绍iOS引用与指针有什么区别?相关面试题,主要包含被问及iOS引用与指针有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 引用必须被初始化,指针不必。 引用初始化以后不能被改变,指针可以改变所指的对象。 不存在指向空值的引用,但是存在指向空值的指针。

  • 在C中不使用sizeof查找数组大小的问题中,asker将int数组视为int数组的数组,方法是获取地址,然后指定数组索引为1:

  • 另外,为什么autowiring名字不起作用?

  • 问题内容: 我习惯于进行Java编程,在编程时,您无需真正考虑指针。但是,此刻我正在用C ++编写程序。在创建具有其他类成员的类时,何时应该使用指针,何时不应该使用指针?例如,什么时候我想这样做: 与此相反: 问题答案: 首先避免指针。 在以下情况下使用它们: 您想使用Pimpl习惯用法或抽象工厂。 该实例实际上是由程序的其他部分管理的,而该类仅需要能够访问它。 您想推迟对象的构建(即,您想 在