为什么一个函数不是一个对象?
我读了标准n4296(Draft)§1.8第7页:
一个对象是一个存储区域。 [注意:函数不是一个对象,不pipe它是否以对象的方式占用存储空间。 – 注意]
我花了一些时间在网上寻找这样的排斥的一个很好的理由,没有运气。 也许是因为我不完全了解对象。 所以:
- 为什么一个函数不是一个对象? 它有什么不同?
- 这是否与仿函数(函数对象)有任何关系?
很多差异归结为指针和寻址。 在C ++中,指向函数的指针和指向对象的指针是严格分离的事物。
C ++要求您可以将指针转换为任何对象types为void
的指针,然后将其转换为原始types,结果将等于您以“2”开头的指针。 换句话说,不pipe他们是怎么做的,实现必须确保从指针到对象types到指针到空的转换是无损的,所以无论原来是什么,它包含的任何信息都可以被重新创build,所以你可以通过从T*
转换为void *
并返回到T*
来获得和你一样的指针。
这不是一个指向函数的指针 – 如果你把一个函数的指针转换成void *
,然后把它转换回一个指向函数的指针,那么在这个过程中可能会丢失一些信息。 您可能无法取回原始指针,并且取消引用您所做的取回给了您未定义的行为(简而言之,不要这样做)。
然而,对于它的价值,你可以将一个函数的指针转换为一个指向不同types函数的指针,然后把结果转换回原来的types,并保证结果与你开始的一样用。
虽然这与手头的讨论不是特别相关,但还有一些其他的差异值得注意。 例如,您可以复制大多数对象 – 但不能复制任何function。
至于与函数对象的关系如下:好吧,实际上没有太多的一点:函数对象支持看起来像函数调用的语法 – 但它仍然是一个对象,而不是一个函数。 所以,一个指向函数对象的指针仍然是一个指向对象的指针。 例如,如果将一个转换为void *
,然后将其转换回原始types,则仍然保证返回原始指针值(对于指向函数的指针不是这样)。
至于为什么指向函数的指针(至less可能)与指向对象的指针不同:部分指向现有系统。 例如,在MS-DOS(其中包括)有四个完全独立的内存模型:小型,中型,紧凑型和大型。 小型号使用16位寻址function或数据。 中等数据使用16位地址,代码使用20位地址。 Compact反转(16位地址代码,20位地址数据)。 大量用于代码和数据的20位地址。 所以,无论是在紧凑型还是中型模型中,在指向代码的指针和指向函数的指针之间进行转换确实会导致问题。
最近,相当数量的DSP使用完全独立的存储器总线来存储代码和数据,并且(和MS-DOS存储器模型一样)它们通常是不同的宽度,在二者之间进行转换可能会失去信息。
- 这些特定的规则来自C语言的C ++,所以在C语言中也是如此,无论价值如何。
- 虽然它不是直接需要的,但事情的工作方式,对于从原始types转换为指向
char
和back的指针,无论值多less,都是相同的。
为什么一个函数不是一个对象? 它有什么不同?
为了理解这一点,让我们从所涉及的抽象的angular度从下到上。 所以,你有你的地址空间,通过它你可以定义内存的状态, 我们必须记住,从根本上说,这是关于你操作的这个状态。
好吧,让我们在抽象的方面稍微高一点。 我没有考虑任何由编程语言( 如对象,数组等 )所强加的抽象,而只是作为一名外行,我想保留一部分内存的logging,我们称它为Ab1
,另一个称为Ab2
。
两者都有一个基本的国家,但我打算以不同的方式操纵/利用国家。
不同的是…为什么和如何?
为什么?
由于我的要求(例如执行2个数字的添加并将结果存回)。 我将使用Ab1
作为长时间使用状态,使用Ab2
作为相对较短的使用状态。 因此,我将为Ab1
创build一个状态( 添加2个数字 ),然后使用此状态填充Ab2
的某些状态( 暂时复制它们 ),然后对Ab2
进行进一步操作( 添加它们 )并保存一部分结果Ab2
到Ab1
( 添加的结果 )。 发布Ab2
变得无用,我们重置其状态。
怎么样?
我将需要对这两个部分进行一些pipe理,以跟踪从Ab1
select哪些单词并复制到Ab2
等。 在这一点上,我意识到我可以使其工作来执行一些简单的操作,但是一些严重的事情需要一个布局的规范来pipe理这个内存。
所以,我寻找这样的pipe理规范,事实certificate,有一个更好的devise,这些规范( 有些内置内存模型,其他人提供了自己pipe理内存的灵活性 )的各种存在。 事实上,因为他们( 甚至没有直接pipe理内存的指令 )已经成功地为这个长期存储的存储定义了封装,并规定了如何以及何时可以创build和销毁这些封装。
对Ab2
也是一样,但是他们呈现的方式让我觉得这跟Ab1
有很大的不同。 事实上,事实certificate是这样的。 他们使用堆栈来处理Ab2
状态,并为Ab1
保留内存。 Ab2
在一段时间后死亡(执行完毕后)。
此外,您定义如何处理Ab2
是通过另一个称为Ab2_Code
存储部分来完成的, Ab1
规范同样涉及到Ab1_Code
我会说,这太棒了! 我得到如此多的便利,使我能够解决这么多问题。
现在,我仍然从外行的angular度来看,所以我并不感到惊讶,但是如果你从头到尾提出问题的话,事情会变得有些困难。 这就是你的情况 )
顺便说一句,我忘了提及Ab1
被正式称为一个对象 , Ab2
是一个函数堆栈,而Ab1_Code
是类定义 , Ab2_Code
是函数定义代码。
正是由于PL所强加的这些差异,你发现它们是如此不同( 你的问题 )
注意:不要把我的Ab1
/ Object
表示看作是一个长期的存储抽象,作为一个规则或者一个具体的东西 – 这是从外行的angular度来看的。 编程语言在pipe理对象的生命周期方面提供了更多的灵活性。 所以,对象可以像Ab1
那样部署,但是它可以更多。
这是否与仿函数(函数对象)有任何关系?
请注意,第一部分的答案对于很多通用的编程语言(包括C ++)都是有效的,这部分必须专门用C ++(你所引用的规范)。 所以你有指向一个函数的指针,你也可以有一个指向一个对象的指针。 它只是C ++定义的另一个编程构造。 请注意,这是关于有一个指向 Ab1
, Ab2
的指针来操作它们,而不是有另一个明确的抽象行为。
你可以在这里阅读它的定义和用法:
C ++ Functors – 及其用法
让我用简单的语言(术语)来回答这个问题。
一个函数包含什么?
它基本上包含做某事的指示。 在执行指令时,函数可以临时存储和/或使用一些数据,并可能返回一些数据。
虽然说明存储在某个地方 – 这些说明本身不被视为对象。
那么,什么是对象?
一般来说,对象是包含数据的实体 – 通过函数(指令)进行操作/更改/更新。
为什么区别?
因为计算机的devise方式使得指令不依赖于数据。
为了理解这一点,我们来考虑一个计算器。 我们用计算器做不同的math运算。 说,如果我们想添加一些数字,我们提供的数字计算器。 不pipe数字是什么,计算器会按照相同的指令以相同的方式添加它们(如果结果超出计算器的存储容量,则会显示一个错误 – 但这是因为计算器限制了存储结果(数据),而不是因为其添加说明)。
计算机的devise也是类似的。 这就是为什么当你在一些与函数兼容的数据上使用库函数(例如qsort()
)时,你会得到和你期望的相同的结果 – 如果数据改变,函数的function不会改变 -因为该function的指示保持不变。
函数和函数之间的关系
function是指令集; 并且在执行时,可能需要一些临时数据来存储。 换句话说,一些对象可能在执行该function时临时创build。 这些临时对象是仿函数。