使用引用-指针作为函数参数的危险性

问题描述 投票:0回答:1

谁能告诉我这样做是否安全,因为我觉得不安全。

class A
{
public:
    A(int*& i) : m_i(i)
    {}

    int*& m_i;
};

class B
{
public:
    B(int* const& i) : m_i(i)
    {}

    int* const & m_i;
};


int main()
{
    int i = 1;
    int *j = &i;

    A a1(j);   // this works (1)
    A a2(&i);  // compiler error (2)
    B b(&i);   // this works again (3)
}

我明白为什么(1)能成功 我们传递的是一个指针,函数接受它作为引用。

但是为什么(2)不工作呢?从我的角度来看,我们传递的是同一个指针,只是没有先把它赋值给一个指针变量。我的猜测是 &i 是一个r值,没有自己的记忆,所以引用不能有效。我可以接受这个解释(如果这是真的)。

但是为什么(3)会编译呢?这岂不是意味着我们允许无效的引用,所以 b.m_i 基本上是没有定义的?

我是否完全理解错了这是如何工作的?我问这个问题是因为我得到了奇怪的单元测试失败,我只能用指针变得无效来解释。它们只发生在一些编译器上,所以我认为这一定是标准之外的东西。

所以我的核心问题基本上是。是否使用 int* const & 中的函数参数,这本身就很危险,应该避免,因为不怀好意的调用者可能总是用 &i 就像使用常规指针参数一样?

补充一下。 正如@franji1所指出的,以下是一个有趣的思路,可以理解这里发生的事情。我修改了main(),改变了内部指针,然后打印了成员的 m_i:

int main()
{
    int i = 1;
    int *j = &i;  // j points to 1

    A a1(j);
    B b(&i);

    int re = 2;
    j = &re;  // j now points to 2

  std::cout << *a1.m_i << "\n";  // output: 2
  std::cout << *b.m_i << "\n";  // output: 1
}

然而,由于b不能知道j已经被修改,它似乎持有一个对 "个人 "指针的引用,但我担心的是,它在标准中没有很好地定义,所以可能有一些编译器对这个 "个人 "指针没有定义。谁能证实这一点?

c++ pointers reference
1个回答
2
投票

A的构造函数取一个 非宪法参照物 到一个 int* 指针。 A a1(j); 工作,因为 j 是一个 int* 变量,所以满足引用。 而 j 寿终正寝 a1所以 A::m_i 成员的一生都可以安全使用。a1.

A a2(&i); 编译失败,因为虽然 &i 是一个 int*, operator& 返回一个 暂时 值,它不能被绑定到一个非const引用上。

B b(&i); 编译时,因为 B的构造函数取一个 参考 变成 int*,它可以被绑定到一个临时变量上。 临时的寿命将通过绑定到构造函数的 i 参数,但一旦构造函数退出,该参数就会失效,因此 B::m_i 成员将是一个 悬指 并且在构造函数退出后根本无法安全使用。


1
投票

j 是一个lvalue,因此它可以被绑定到一个非const lvaue引用上。

&i 是一个prvalue,因此它不能被绑定到非const lvalue引用上。这就是为什么(2)不能编译的原因。

&i 是一个 prvalue(临时),它可以被绑定到一个 const lvalue 引用上。将一个prvalue绑定到一个引用上,可以将临时的寿命延长到引用的寿命。在这种情况下,这个临时的寿命被延长到构造函数参数的寿命。i. 然后您可以初始化参考文献 m_ii 构造参数)(这是一个对临时的引用),但由于 i 是一个l值,临时成员的寿命不会被延长。最后,你会得到一个引用成员 m_i 绑定到一个没有生命的对象上。你有一个 悬指. 访问 m_i 从现在开始(在构造函数完成后)是未定义行为。


简单的表,可以绑定到什么引用。C++11 rvalue reference vs const reference


-1
投票

指针是一个内存地址。为了简单起见,可以把指针看成是 uint64_t 变量,持有一个代表什么的内存地址的数字。引用只是某个变量的别名。

在例子(1)中,你将一个指针传递给构造函数,期望得到一个对指针的引用。它的工作原理是,编译器得到指针值所在的内存地址,并将其传递给构造函数。构造函数得到这个数字并创建一个别名指针。结果你得到的是一个别名为 j. 如果您修改 j 那么 m_i 也会被修改。您可以修改 m_i 也指向其他东西。

在例子(2)中,你向构造函数传递了一个数字值,期望得到一个指向指针的引用,因此,构造函数得到的不是一个地址,而是一个地址,编译器没有办法满足构造函数的签名。所以,构造函数得到的不是一个地址的地址,而是一个地址,编译器没有办法满足构造函数的签名。

在例子(3)中,你传递了一个数字值给构造函数,期望得到一个恒定的指针引用。恒量引用是一个固定的数字,只是一个内存地址。在这种情况下,编译器理解了这个意图,并提供了在构造函数中设置的内存地址。结果你得到了固定的别名的 i.

编辑(为清楚起见)。(2)和(3)之间的区别是: &i 不是对 int*但它是一个有效的 常设 提及 int*.

© www.soinside.com 2019 - 2024. All rights reserved.