从 C++ 中的函数返回引用的推荐方法
Recommended way of returning a reference from a function in C++
我一直在使用这种方法从 C++ 中的函数返回引用。但是,我怀疑有更好的模式来执行这样的操作。另外,我猜想这种方法意味着内存泄漏。
class A {};
A& return_instance_of_A(){
A* result = new A();
return *result;
}
使用 shared_ptr 会是更好的选择吗?
What is the recommended way of returning a reference from a function?
语法上,只是 return 一个引用。
int& myFunction() { .... }
引用的行为几乎像指针。你的例子虽然有一些问题。
您分配的对象在某个时候需要删除,通常这是通过指针处理的。规范地,收到您以后需要的参考是非常奇怪的delete
。
在现代 C++ 中以这种断开连接的方式处理内存分配也不常见。我同意你 returning a shared_ptr
的建议。这使得所有权明确,并且不会像您的示例那样无意中泄漏内存。
您的示例不一定导致内存泄漏,但它很尴尬,因为您对调用者提出了某些要求(即,delete
returned 对象),编译器不强制执行。
编辑,以解决那些建议 return 值的人:这仅取决于调用者的要求。许多小型/实用对象,如 Rectangle
或 Size
旨在按值传递,这使事情变得非常简单和直观。
一个实际的例子是这样的:
inline Rect make_square_rect(int left, int right, int width)
{
return Rect(left, right, width, width);
}
绝对像这样的函数最好按值return。请注意此功能与构造函数有多么相似......
对于 TcpConnection
或 Window
等其他更大、更承诺和有状态的对象,这种相似性变得更加明显。所有权和内存管理的问题被放大了。
同样适用于任何不能 copied/moved。
因此,创建一个新的 Window
不能像 Rect
那样随意。从像你这样的函数创建一个 Rect
并不真正关心所有权问题,因为复制 Rect
对象是多么便宜和简单。但是,如果您的函数 return 类似于 Window
,那么您的函数自然会解决所有权问题——可能通过 returning a 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) { ... }
没有好的方法可以将它们变成构造函数。你需要一个符号名来区分这些函数,构造函数不能有名字。
作为独立函数编写,您不得不决定所有权行为。您只需权衡 pros/cons,而且主要是:考虑调用者希望如何使用该对象。如果调用者想要一个持久的、有状态的长寿命对象,那么 return 一个 shared_ptr
。如果调用者将该对象用作中间值类型(我认为 BigInteger
绝对是),则按值 return。
我一直在使用这种方法从 C++ 中的函数返回引用。但是,我怀疑有更好的模式来执行这样的操作。另外,我猜想这种方法意味着内存泄漏。
class A {};
A& return_instance_of_A(){
A* result = new A();
return *result;
}
使用 shared_ptr 会是更好的选择吗?
What is the recommended way of returning a reference from a function?
语法上,只是 return 一个引用。
int& myFunction() { .... }
引用的行为几乎像指针。你的例子虽然有一些问题。
您分配的对象在某个时候需要删除,通常这是通过指针处理的。规范地,收到您以后需要的参考是非常奇怪的delete
。
在现代 C++ 中以这种断开连接的方式处理内存分配也不常见。我同意你 returning a shared_ptr
的建议。这使得所有权明确,并且不会像您的示例那样无意中泄漏内存。
您的示例不一定导致内存泄漏,但它很尴尬,因为您对调用者提出了某些要求(即,delete
returned 对象),编译器不强制执行。
编辑,以解决那些建议 return 值的人:这仅取决于调用者的要求。许多小型/实用对象,如 Rectangle
或 Size
旨在按值传递,这使事情变得非常简单和直观。
一个实际的例子是这样的:
inline Rect make_square_rect(int left, int right, int width)
{
return Rect(left, right, width, width);
}
绝对像这样的函数最好按值return。请注意此功能与构造函数有多么相似......
对于 TcpConnection
或 Window
等其他更大、更承诺和有状态的对象,这种相似性变得更加明显。所有权和内存管理的问题被放大了。
同样适用于任何不能 copied/moved。
因此,创建一个新的 Window
不能像 Rect
那样随意。从像你这样的函数创建一个 Rect
并不真正关心所有权问题,因为复制 Rect
对象是多么便宜和简单。但是,如果您的函数 return 类似于 Window
,那么您的函数自然会解决所有权问题——可能通过 returning a 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) { ... }
没有好的方法可以将它们变成构造函数。你需要一个符号名来区分这些函数,构造函数不能有名字。
作为独立函数编写,您不得不决定所有权行为。您只需权衡 pros/cons,而且主要是:考虑调用者希望如何使用该对象。如果调用者想要一个持久的、有状态的长寿命对象,那么 return 一个 shared_ptr
。如果调用者将该对象用作中间值类型(我认为 BigInteger
绝对是),则按值 return。