从 C++ 中的函数返回引用的推荐方法

Posted

技术标签:

【中文标题】从 C++ 中的函数返回引用的推荐方法【英文标题】:Recommended way of returning a reference from a function in C++ 【发布时间】:2015-05-04 13:57:33 【问题描述】:

我一直在使用这种方法从 C++ 中的函数返回引用。但是,我怀疑有更好的模式来执行这样的操作。另外,我猜这种方法意味着内存泄漏。

class A ;
A& return_instance_of_A()
    A* result = new A();
    return *result;

使用 shared_ptr 会是更好的选择吗?

【问题讨论】:

为什么要返回引用? 您的代码没有任何问题,因为您是在堆上创建对象。但是为什么不能返回一个指针呢? 为什么不直接使用A return_instance_of_A() A result; return A; @Codor 因为这会在离开函数时创建一个新对象(复制构造函数)。 (如果不使用返回值优化) 我支持@Codor。应该使用按值返回堆栈创建的对象。 【参考方案1】:

从函数返回引用的推荐方式是什么?

从语法上讲,只返回一个引用。

int& myFunction()  .... 

引用的行为几乎像指针。你的例子虽然有一些问题。

您分配的对象需要在某个时候被删除,通常这是通过指针来处理的。典型地,收到您以后需要delete 的参考是非常奇怪的。

在现代 C++ 中,以这种不连贯的方式处理内存分配也不常见。我同意您返回shared_ptr 的建议。这使得所有权明确,并且不会像您的示例那样无意中泄漏内存。

您的示例不会必然导致内存泄漏,但这很尴尬,因为您对调用者(即,对delete返回的对象)提出了编译器未强制执行的某些要求.


编辑,以解决建议按值返回的人们:它仅取决于调用者的要求。许多小型/实用对象,例如RectangleSize,都是按值传递的,这使得事情变得非常简单和直观。

一个实际的例子是这样的:

inline Rect make_square_rect(int left, int right, int width)

  return Rect(left, right, width, width);

绝对像这样的函数最好按值返回。请注意此功能与构造函数有多么相似...

对于其他更大、更承诺和有状态的对象,如TcpConnectionWindow,这种相似性变得更加清晰。所有权和内存管理的问题被放大了。

任何无法复制/移动的东西也是如此。

因此,创建新的Window 不能像Rect 那样随意。从像您这样的函数创建Rect 并不太关心所有权问题,因为复制Rect 对象非常便宜和简单。但是,如果您的函数返回类似Window 的内容,那么您的函数很自然会处理所有权问题——很可能通过返回shared_ptr<Window>


编辑#2:构造函数的性质

针对你的cmets,我再次指出,这些函数非常类似于构造函数。这些函数实际上应该只是设置一个对象以供首次使用 - 但我们坐在这里试图决定该函数应该如何处理所有权/复制。

真的,这正是构造函数应该做的。

struct BigInteger

    BigInteger(int initial_value)  ... 
;

在这里,构造函数不需要处理我们正在谈论的概念。调用者决定他想如何处理所有权:

BigInteger* ptr = new BigInteger(42);
BigInteger val = BigInteger(42);

编写为构造函数,这可以处理这两种情况。在我看来,这种情况令人讨厌的是构造函数不能在 C++ 中命名。例如,假设您正在编写这些函数:

BigInteger make_big_integer_by_multiplying(int a, int b)  ... 
BigInteger make_big_integer_by_adding(int a, int b)  ... 

没有什么好办法可以把它们变成构造函数。你需要一个符号名来区分这些函数,而构造函数不能有名字。

作为独立函数编写,您必须决定所有权行为。您必须权衡利弊,主要是:考虑调用者希望如何使用该对象。如果调用者想要一个持久的、有状态的长寿命对象,则返回一个shared_ptr。如果调用者将对象用作中间值类型(我认为BigInteger 绝对是),则按值返回。

【讨论】:

你的解释很有启发性。感谢您的回复。实际上,我正在实现一个 BigInteger 类,作为一个实践练习,以了解更多关于 C++ 中涉及的最佳设计决策的信息。我将 BigInteger 建模为私有 vector 并且想知道在极端情况下我可以得到的行为。让我们假设需要构建副本的情况,即需要从头开始构建整个对象的痛苦。在这种情况下,为了简单起见,维护多个值优于多个 shared_ptr,对吧? 在我的回答中添加了更多的喋喋不休:D 关于性能,老实说,除非您立即发现明显有问题的东西,否则我什至不会考虑性能。首先关注简洁、富有表现力的代码设计,并在出现性能问题时处理它们。请记住,副本都将在调用者的代码中。如果调用者想避免复制,那么调用者应该......停止制作不必要的副本。与编写此函数无关。 随着这个讨论的发展,我真的认为现在返回引用的用例非常有限,可能操作符重载签名让我有点困惑,我开始应用这种模式,它没有多大作用完全有感觉。

以上是关于从 C++ 中的函数返回引用的推荐方法的主要内容,如果未能解决你的问题,请参考以下文章

返回后指针不会从函数返回到它的引用(保持为空)C++

C ++:从函数、返回类型或引用中使用和返回字符数组?

C++ 把引用作为函数返回值

C++ 引用与返回值

c++ 函数返回引用问题

C++ 函数返回引用