为什么函数不是对象?

时间:2017-05-15 03:49:19

标签: c++ function object functor

我读了标准n4296(草案)§1.8第7页:

  

对象是存储区域。 [注意:一个函数不是一个对象,   无论它是否以某种方式占据存储空间   对象呢。 - 后注]

我在网上花了好几天寻找这种排斥的充分理由,没有运气。也许是因为我不完全理解对象。所以:

  1. 为什么函数不是对象?它有何不同?
  2. 这与仿函数(函数对象)有什么关系吗?

3 个答案:

答案 0 :(得分:43)

很多不同之处在于指针和寻址。在C ++中,指向函数和指向对象的指针是完全不同的东西。

C ++要求您可以将指向任何对象类型的指针转​​换为指向void的指针,然后将其转换回原始类型,结果将等于您启动的指针²。换句话说,无论它们究竟是如何做的,实现都必须确保从指针到对象类型到指针到无效的转换是无损的,因此无论原始内容是什么,它包含的任何信息都可以重新创建,以便您可以通过从T*转换为void *并返回T*来获得与开始时相同的指针。

对于函数指针,为true - 如果你接受一个指向函数的指针,将它转换为void *,然后将其转换回指向一个函数的指针功能,您可能会在此过程中丢失某些信息。您可能无法取回原始指针,并且取消引用您返回的内容会给您带来未定义的行为(简而言之,请不要这样做)。

对于它的价值,可以,但是,将指向一个函数的指针转换为指向不同类型函数的指针,然后将该结果转换回原始类型,然后你就是保证结果与您开始时的结果相同。

尽管与手头的讨论并不特别相关,但还有一些其他差异可能值得注意。例如,您可以复制大多数对象 - 但是您无法复制任何函数。

就与函数对象的关系而言:好吧,除了一点之外真的没有多少:函数对象支持看起来像函数调用的语法 - 但它仍然是对象,而不是函数。因此,指向函数对象的指针仍然是指向对象的指针。例如,如果将一个转换为void *,然后将其转换回原始类型,则仍然可以保证返回原始指针值(对于指向函数的指针,这不是真的) )。

至于为什么指向函数的指针(至少可能)与指向对象的指针不同:它的一部分归结为现有系统。例如,在MS-DOS(以及其他)上,有四个完全独立的内存模型:小型,中型,紧凑型和大型。小型号使用16位寻址功能或数据。中,数据使用16位地址,代码使用20位地址。紧凑反转(代码为16位地址,数据为20位地址)。大量使用20位地址代码和数据。因此,无论是在紧凑型还是中型模型中,在指向代码的指针和指向函数的指针之间进行转换确实会导致问题。

最近,相当数量的DSP使用完全独立的内存总线来代码和数据(和MS-DOS内存模型一样)它们通常是不同的宽度,两者之间的转换可能会丢失信息。

  1. 这些特殊规则来自C的C ++,所以在C中也是如此,无论价值多少。
  2. 虽然它不是直接需要的,但就事情的运作方式而言,对于从原始类型转换为指向char并返回的指针,几乎同样适用于任何值得的东西。

答案 1 :(得分:5)

  

为什么函数不是对象?它有何不同?

要理解这一点,请在涉及的抽象方面从下到上移动。所以,你有地址空间,你可以通过它来定义内存的状态,我们必须记住,从根本上说,它是关于你操作的状态的全部。

好的,让我们在抽象方面稍微提高一点。我还没有考虑编程语言所带来的任何抽象(就像对象,数组等),但作为一个外行人,我想保留一部分内存的记录,让我们称之为 Ab1 ,另一个名为 Ab2

两者都有一个基本的状态,但我打算以不同的方式操纵/使用状态

不同......为什么和如何?

为什么?

由于我的要求(例如,执行添加2个数字并将结果存储回来)。我将使用use Ab1作为长使用状态,Ab2作为相对较短的使用状态。因此,我将为Ab1添加2个数字)创建一个状态,然后使用此状态填充Ab2的某些状态(复制它们暂时)并对Ab2进行进一步操作(添加)并将结果Ab2的一部分保存到Ab1添加结果)。帖子Ab2变得无用,我们重置其状态。

如何?

我需要管理这两个部分,以便跟踪从Ab1中选择哪些字词并复制到Ab2等等。在这一点上,我意识到我可以使它能够执行一些简单的操作,但严重的事情需要一个规范的规范来管理这个内存。

所以,我寻找这样的管理规范,结果发现存在各种这些规范(有一些内置内存模型,其他提供了自己管理内存的灵活性)更好的设计。事实上,因为他们(甚至没有指示如何直接管理内存)已经成功地定义了这个长期存储的封装以及如何以及何时创建和销毁它的规则。

Ab2同样如此,但他们呈现它的方式让我觉得这与Ab1有很大的不同。事实上,事实证明是。它们使用堆栈对Ab2进行状态操作,并为堆Ab1保留内存。一段时间后Ab2死亡。(完成执行后)。

此外,您定义如何处理Ab2的方式是通过另一个名为Ab2_Code的存储部分完成的,Ab1的规范也涉及Ab1_Code

我想说,这太棒了!我得到了很多便利,这让我可以解决很多问题。

现在,我仍然从一个外行人的角度来看,所以我不会感到惊讶真的经历了这一切的思考过程,但如果你自上而下地质疑事情,事情可以得到一点点很难透视。(我怀疑你的案件发生了什么

BTW,我忘了提到Ab1正式称为对象Ab2 函数堆栈,而Ab1_Code类定义Ab2_Code函数定义代码。

正是由于PL所施加的这些差异,你发现它们是如此不同。(你的问题

注意:不要将Ab1 / Object的表示作为长期存储抽象作为规则或具体事物 - 它是从外行的角度来看。编程语言在管理对象的生命周期方面提供了更大的灵活性。因此,可以像Ab1那样部署对象,但它可以更多。

  

这与仿函数(函数对象)有什么关系吗?

请注意,第一部分答案通常适用于许多编程语言(包括C ++),这部分必须专门针对C ++(您引用的规范)。所以你有一个指向函数的指针,你也可以有一个指向对象的指针。它只是C ++定义的另一种编程结构。请注意,这是关于Ab1Ab2指针来操纵它们,而不是让另一个不同的抽象来作用。

您可以在此处阅读其定义和用法:

C++ Functors - and their uses

答案 2 :(得分:0)

让我用更简单的语言(术语)回答这个问题。

功能包含什么?

它基本上包含做某事的说明。在执行指令时,该函数可以临时存储和/或使用某些数据 - 并可能返回一些数据。

虽然指令存储在某处 - 但这些指令本身不被视为对象。

那么,对象是什么?

通常,对象是包含数据的实体 - 通过函数(指令)进行操作/更改/更新。

为什么会有区别?

因为计算机的设计方式使得指令不依赖于数据。

要理解这一点,让我们考虑一下计算器。我们使用计算器进行不同的数学运算。比如,如果我们想要添加一些数字,我们会向计算器提供数字。无论数字是多少,计算器都会按照相同的指令以相同的方式添加它们(如果结果超出了计算器的存储容量,它将显示错误 - 但这是因为计算器存储结果的限制(数据),而不是因为它的添加说明。)

计算机的设计方式与此类似。这就是为什么当你对一些与函数兼容的数据使用库函数(例如qsort())时,你会得到与你期望的相同的结果 - 如果数据,函数的功能不会改变更改 - 因为函数的指令保持不变。

函数和仿函数之间的关系

功能是指令集;当它们被执行时,可能需要一些临时数据来存储。换句话说,在执行函数时可能会临时创建某些对象。这些临时对象是仿函数。