54

通过引用传递函数时取消引用指针会发生什么?

这是一个简单的例子

int& returnSame( int &example ) { return example; }

int main()
{
  int inum = 3;
  int *pinum = & inum;

  std::cout << "inum: " <<  returnSame(*pinum) << std::endl;

  return 0;          

}

是否产生了临时对象?

4

3 回答 3

50

取消引用指针不会创建副本;它创建一个指向指针目标的左值。这可以绑定到左值引用参数,因此函数接收对指针指向的对象的引用,并返回对该对象的引用。这种行为是明确定义的,并且不涉及临时对象。

如果它按值获取参数,那么这将创建一个本地副本,并且返回对它的引用将是不好的,如果访问它会给出未定义的行为。

于 2012-07-05T16:29:24.053 回答
33

书面问题的答案

不,此行为已定义。当指针类型被取消引用或使用引用类型时,不会调用构造函数,除非程序员明确指定,如以下代码片段所示,其中new运算符调用该int类型的默认构造函数。

int* variable = new int;

至于真正发生的事情,正如所写,returnSame(*pinum)inum. 如果您想自己验证这一点,可以使用以下代码段:

returnSame(*pinum) = 10;
std::cout << "inum: " << inum << std::endl;

进一步的分析

我将从更正您提供的代码开始,它看起来不像您在发布之前尝试编译的代码。编辑后,剩下的一个错误在第一行:

int& returnSame( int &example ) { return example; } // semi instead of colon

指针和引用

编译器以相同的方式处理指针和引用,它们的使用不同,而不是它们的实现。指针类型和引用类型将其他东西的位置作为它们的值存储。指针解引用(使用*or->运算符)指示编译器生成代码以跟随指针并在它所引用的位置而不是值本身上执行操作。取消引用指针时不会分配新数据(不调用构造函数)。

使用引用的工作方式几乎相同,除了编译器自动假定您需要该位置的值而不是该位置本身。事实上,不可能以与指针允许您相同的方式引用引用指定的位置:一旦分配,引用就不能重新安装(更改)(即,不依赖于未定义的行为),但是您仍然可以通过对其使用&运算符来获取它的值。甚至可以有一个 NULL 引用,尽管处理这些特别棘手,我不建议使用它们。

片段分析

int *pinum = & inum;

创建一个指向现有变量 inum 的指针。指针的值是存储 inum 的内存地址。创建和使用指针不会隐式调用指向对象的构造函数,永远不会。这个任务留给程序员。

*pinum

取消引用指针有效地产生了一个常规变量。这个变量在概念上可能占用另一个命名变量使用的相同空间,也可能不占用。在这种情况下,*pinuminum是相同的变量。当我说“产生”时,重要的是要注意没有调用构造函数。这就是为什么你必须在使用它们之前初始化指针:指针解引用永远不会分配存储空间。

returnSame(*pinum)

此函数接受一个引用并返回相同的引用。意识到这个函数也可以用指针编写是有帮助的,并且行为方式完全相同。引用也不执行任何初始化,因为它们不调用构造函数。但是,未初始化的引用是非法的,因此通过它们进入未初始化的内存并不像使用指针那样常见。可以通过以下方式重写您的函数以使用指针:

int* returnSamePointer( int *example ) { return example; }

在这种情况下,您不需要在传递指针之前取消引用它,但您需要在打印之前取消引用函数的返回值:

std::cout << "inum: " <<  *(returnSamePointer(pinum)) << std::endl;

空引用

声明 NULL 引用是危险的,因为尝试使用它会自动尝试取消引用它,这将导致分段错误。但是,您可以安全地检查引用是否为空引用。同样,我强烈建议不要使用这些。

int& nullRef = *((int *) NULL);      // creates a reference to nothing
bool isRefNull = (&nullRef == NULL); // true

概括

  • 指针和引用类型是完成同一件事的两种不同方式
  • 适用于一个的大多数陷阱适用于另一个
  • 在任何情况下,指针和引用都不会隐式调用被引用值的构造函数或析构函数
  • 只要正确初始化指针,声明对取消引用指针的引用是完全合法的
于 2012-07-05T16:06:24.040 回答
2

A compiler doesn't "call" anything. It just generates code. Dereferencing a pointer would at the most basic level correspond to some sort of load instruction, but in the present code the compiler can easily optimize this away and just print the value directly, or perhaps shortcut directly to loading inum.

Concerning your "temporary object": Dereferencing a pointer always gives an lvalue.

Perhaps there's a more interesting question hidden in your question, though: How does the compiler implement passing function arguments as references?

于 2012-07-05T15:06:44.973 回答