【问题标题】:Using the same Reference for a new object对新对象使用相同的引用
【发布时间】:2015-03-30 11:31:00
【问题描述】:

在搜索一些松散相关的东西时,我偶然发现了这句话:

并且一个引用可以比一个对象更长寿,并且可以用来引用一个在同一地址创建的新对象。

来自这个answer

现在,我一直都知道引用是不可变的,并且只需要初始化一次就可以了。 阅读上面的引用,可能比我更有经验的人,让我想知道我是否遗漏了什么。

那句话是为了完整而实际上不适用吗?

在某些模式或情况下,人们会经历在特定内存地址中登陆相同类型的新对象只是为了进行切换以供参考的痛苦吗? (这对我来说似乎非常危险,更不用说在最好的时候令人费解了)。

【问题讨论】:

  • 我不确定“实际上不适用”,但我敢打赌这是百万分之一的事情之一。 99.999999% 的程序员永远不会这样做
  • 线程正在讨论对指针的引用。我想在这种情况下上下文很有价值。
  • 我倾向于认为它在实际情况下是无用的,甚至是危险的。它可能就在那里,因为编译器无法强制引用与它们的对象一起死。
  • @remyabel 答案对我来说似乎比那个更通用,这就是为什么它让我想知道(我仍然是)是否有一个我不知道的漂亮技巧。它发生在(对我来说)之前。

标签: c++ reference


【解决方案1】:

我认为这仅在展示位置 new 的背景下才有意义。如果引用指向的对象是使用位置new 创建的,则应该可以销毁该对象并再次使用位置new 在同一位置创建一个新对象。不过,目前我没有看到任何不使用指针的直接理由。

【讨论】:

  • 事实上,这个声明是对标准部分关于存储生命周期和对象生命周期的解释,并且通过在同一内存区域上调用placement new 来重用是一个主要关注点。但是没有删除,删除是为了获取内存。与放置 new 对应的是显式的析构函数调用。
  • s/获取内存/释放内存/...错误的 Swype 自动更正:(
【解决方案2】:

悬空引用的问题与悬空指针的问题本质上是一样的。

例如两个函数

int &GetReference()
{
    int x;    //  local variable

    return x;
}

int *GetPointer()
{
    int x;

    return &x;
} 

如果使用返回的引用或取消引用指针,调用者会遇到完全相同的问题。

int &r = GetReference();
int *p = GetPointer();

r = 52;
*p = 42;

这两个赋值都表现出未定义的行为,因为就程序而言,名为 x 的变量(在两个函数内)不再存在。但是,代码似乎可以正常工作。

通过释放动态分配的内存(C 中的 free(),C++ 中的 operator delete)来创建悬空引用或指针也会发生同样的情况。

如果其他代码(最终)使用该内存(例如,表示另一个变量,表示一个不相关的对象),则该引用或指针通常可以访问该内存位置的任何内容。这可能会产生值更改的虚假问题(这可能会给使用引用的代码或发现变量或对象正在更改的不相关代码带来惊喜)。

实际上,它不是渴望或使用的东西。这是一个危险的程序缺陷,通常很难修复或调试 - 因为它为两个完全不相关的代码部分提供了一条路径,以影响另一部分使用的数据。

幸运的是,现代编译器通常(如果配置为提供最大警告级别)确实会针对许多可疑结构(例如返回指针或对局部变量的引用)发出警告。

【讨论】:

  • “幸运的是,现代编译器通常确实会针对许多可疑结构发出警告。”我多年的段错误说其他话。
  • 由于各种历史原因,例如懒惰的程序员游说,编译器很少默认配置为提供此类警告。然而,几乎现代编译器都可以配置为发出此类警告。不幸的是,也有编译器无法检测到的情况。
  • 我总是用-Wall 编译。我并没有因此而责怪编译器,但我建议不要过度依赖它的检查。它可以使用的编译时间信息只有这么多,处理指针地址的任何事情在运行时间之外都是非常受限的。
  • 引用的语句是在解释标准中一个更复杂的规则,它增加了“在重用存储之前”的附加要求,这具有排除您正在讨论的用法的效果。你说的一切都是正确的......但与另一个答案中提到的特定保证无关。
  • 当然。但是,编译器检查(如果注意警告)可以帮助避免很多错误。但是你是对的,在某些情况下编译器无能为力。不同作者(Sutter、Myers 等)的许多技术和习语都是关于防止此类问题的。例如,基于 RAII 的技术。
【解决方案3】:

以下是 C++ 标准的完整段落,我正在解释:

如果在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,在原对象占用的存储位置创建一个新对象,一个指向原对象的指针, 引用原始对象的引用,或原始对象的名称将自动引用新对象,一旦新对象的生命周期开始,可以 用于操作新对象,if:

  • 新对象的存储空间正好覆盖了原始对象占用的存储位置,并且
  • 新对象与原始对象的类型相同(忽略顶级 cv 限定符),并且
  • 原始对象的类型不是 const 限定的,如果是类类型,则不包含任何类型为 const 限定或引用类型的非静态数据成员,并且
  • 原始对象是 T 类型的最衍生对象 (1.8),而新对象是 T 类型的最衍生对象(也就是说,它们不是基类子对象)。

“在存储位置创建了一个新对象”当然是放置新的效果,正如 midor 观察到的那样。

【讨论】:

    【解决方案4】:

    在幕后,引用只不过是自动取消引用指针,因此它们会遇到与指针相同的一些问题。引用可能是陈旧的,就像指针一样。引用引用的内存可以被其他对象重用,就像指针引用的内存一样。我想这就是所有有问题的陈述想要表达的意思。

    一个理智的程序员会故意利用这个吗?不太可能,我也不认为评论建议您尝试这样做(尽管从技术上讲这是可能的,使用新位置)。我认为更多的是防止人们按照托管语言的思路进行思考,在托管语言中,仅仅存在引用就足以让对象保持活力。

    【讨论】:

    • 我要解决的误解是引用绑定到对象这一经常被引用但错误的“事实”。它不是;就像一个指针,一个引用被绑定到一个内存位置。
    • 我同意“事实”经常被不恰当地引用,但你走得太远了——它在上下文中是正确的,即使人们经常在上下文中引用它。引用的类型指定了相关内存位置的含义——即导致访问/影响该内存的有效操作(主要是在没有未定义行为的意义上)表现得就像存在该类型的对象一样在那个记忆位置。从语义上讲,这意味着 C++ 引用绑定到一个对象,而该对象又绑定到一个内存位置。
    • @Rob,但该内存位置的对象可能与用于初始化引用的对象不同。因此,引用不绑定到它的初始化对象,它绑定到它的内存位置。请参阅 Ben 对这个问题的回答中引用的 C++ 标准。
    • 该引用的适用性非常有限(根据 Ben 引用的要点)。在与一种误解作斗争时,你是在呈现另一种东西,就好像它总是真实的一样,而它只在有限的情况下才是真实的。因此,您正在用另一种误解取代一种误解。
    猜你喜欢
    • 2012-08-13
    • 2012-04-23
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多