价值与对象
Value vs. Object
[C++17]
当评估prvalue表达式时,标准会说它会产生一个值。在5
的情况下,表达式是一个prvalue,其计算结果为值5
。
但是,当您有一个prvalue时,它主要是一个对象的初始值设定项,例如Foo{}
。这个表达式的值是多少?结果会是由prvalue到xvalue转换创建的临时对象吗?这就引出了我更广泛的问题,即值和对象之间的差异。
[interro.object]/1:
当隐式更改联合的活动成员时,或当创建临时对象时,对象由定义、新表达式创建一个物体在其建造期、使用寿命和破坏期都占据了一个存储区域。
无论prvalue是否具有类似Foo{}
的类类型,作为文字5
,都被视为一个值,如果确实需要,则该值将用于初始化对象,此时该值将具体化为对象。
[class.temporary]/2:
为了避免创建不必要的临时物体。
在同一部分下,您会发现一个列表,描述临时性何时具体化
值是一个抽象概念。一个值与表征或标识该值的一组实现相关联。例如,价值10美元的人可以买一本书或一顿饭。
一个值可以有多个表示形式。例如,10美元可以用硬币表示,也可以作为比特存储在银行账户中。
对象对于值就像银行账户对货币量一样:对象(/银行账户)持有该值的表示(/10$)。这在【basic.types】:中进行了描述
类型T的对象的value表示是参与表示类型T的值的比特集。对象表示中不属于值表示的位是填充位。
然后在[interro.object]中指定一个对象与存储区域相关联:
一个对象在其构建期([class.cdtor])、整个生命周期和销毁期([cclass.cdtor】)中占据存储区域。
如果我们考虑一个抽象机器,它有一个执行操作的中央处理器单元和一个可以存储对象(保存值表示)的分离存储器,那么对象及其值之间的区别就更合理了。当对某个值执行操作时,该值会加载到不同的cpu寄存器中。因此,cpu中的值没有相同的表示形式:一个连续的比特序列,就像它在对象中一样。此外,任何cpu都可以自由地表示寄存器中最适合其需要的值。
当cpu执行一个操作时,它对存储在寄存器中的一段值执行操作。执行该操作后,cpu可以将结果保存在对象内部的内存中,或者继续对该值进行操作。
在对值的操作以及从对象到对象的存储或加载中,操作的分解出现在标准中:
-
加载是左值到右值的转换[conv.lvalue]非函数、非数组类型T的glvalue可以转换为prvalue
-
c++中的所有操作都将产生一系列具有内置含义的基本运算。这些操作大多适用于值(prvalue),而不适用于对象。在执行这些操作之前,应用左值到右值[expr]每当glvalue表达式显示为期望该操作数为prvalue的运算符的操作数时,左值到右值,[…]
-
这些对值进行操作的内置操作的结果总是prvalue(prvalue只是一个不与任何对象关联的值)。然后,结果值可以用作其他内置操作的操作数,或者初始化对象(机器内存中的存储操作),[basic.lval]:prvalue是一个表达式,其求值初始化对象或位字段,或计算运算符的操作数值,具体由其出现的上下文指定
为了说明这一点,让我们分析一下这段简单的代码:
int main(int argc, char* argv[]){
int j = 2*argc+1;
}
2*argc
内置运算符*由两个参数2
和argc
调用。argc
是一个左值,因此应用左值到右值。argc
的值被加载到cpu寄存器中(2可以是立即数),并且执行multiply
操作2*argc
的结果是直接用作第一操作数(2*argc)+(argc)
的prvalue。然后,最后一次操作的结果prvalue用于初始化对象j
:结果值被存储在j
的内存表示中
值是一个概念;物体是一个有生命的东西。对于具有复杂构造函数的类类型,这种区别往往要重要得多,但规则同样适用于所有类型。
考虑一下这个简单的程序:
std::string foo() { return std::string{"Hello"}; }
int main() {
std::string f = foo();
}
foo
不创建对象。创建一个对象需要调用类的构造函数来开始对象的生存期。对于std::string
,这可能涉及到分配内存和复制字符,出于显而易见的原因,我们希望避免这样做太多次。
相反,foo
返回一个值。它返回"用字符"Hello"初始化的字符串"的概念。最终,main
能够采用抽象概念并构造一个对象来表示该值。由于这种区别,只创建了一个对象,因此开始和结束对象生命周期的额外成本只需要支付一次。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 使用std::函数映射对象方法
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 构造对象的歧义
- 使用"std::unordereded_map"映射到"std::list"对象