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

时间:2015-05-04 13:57:34

标签: c++ memory-management memory-leaks

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

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

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

1 个答案:

答案 0 :(得分:6)

  

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

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

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:构造函数性质

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

实际上,这正是构造函数应该做的事情。

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)然后按值返回。