c++ - shared_ptr 内 string_view 的返回值优化
问题描述
很难用语言表达,所以我将直接跳入半伪代码。
我有一个下载功能(http GET),它在我的主代码中被多次调用。
std::string download_data(){
std::shared_ptr<HttpResponse> response = some_http_client->send_request("some_link");
return std::string(response->body()); // response->body() is a std::string_view.
}
http_client
我正在使用的返回 a作为shared_ptr
响应,这个响应(我排除了 HTTP 错误处理的代码,假设它是 200。)包含 a response->body()
,它是 a std::string_view
。
此代码工作正常,但是,我想确保每次调用/返回此函数时都不会复制下载的数据。
我的主要问题:
- 我使用的当前代码是否受返回值优化?(有什么需要做的吗?)
- 如果没有,我可以返回
return response->body();
吗?函数返回后的string_view
内部是否有效?shared_ptr
我考虑过或在旧版本代码中使用的东西:
- 返回
std::string
(与另一个std::string
作为正文返回的 http 客户端)。 - 与
std::move
. - 不要编写函数,只需替换函数体调用此函数的所有位置,直接使用
response->body
,避免返回(我讨厌它)。
这样做的正确方法是什么?
我的工具链:
Ubuntu 20.04 ( GLIBC 2.31
), g++ 10.2
, C++20
.
解决方案
您的代码将使用 RVO。它返回一个与函数返回的类型相同的临时值,这是强制执行 RVO的情况之一。
当然,它仍然需要一份数据副本,作为string
接受 astring_view
作为参数的构造函数的一部分。
你不能仅仅通过string_view
它自己。它只不过是一对指向别人数据的指针。根据您的代码,这几乎可以肯定是 拥有的数据response
,在您可以使用string_view
返回的数据之前,这些数据将过期。
你基本上有两个选择。您可以复制数据,也可以保留它。您当前的代码将其复制一次(感谢 RVO),因此对于这种情况,它是我们所能获得的理想选择。但是,还有另一种方法。我们可以返回一个指向字符串视图的“别名”共享指针。让您的函数返回 a std::shared_ptr<std::string_view>
,我们将进行设置以使其正常工作。
的别名构造函数shared_ptr<T>
如下所示:
template <typename Y>
shared_ptr(const shared_ptr<Y>& custodian, T* ward)
它创建了一个shared_ptr
,当取消引用时,它指向病房。但是,它“拥有”托管人,托管人可以是任何其他类型。在此共享指针被销毁之前,保管人不会被销毁。
要使用它,我们必须创建一个新类,该类包装一个shared_ptr<HttpResponse>
和一个string_view
引用该响应中数据的主体。我会命名它BodyCustodian
以使命名尽可能一致。
struct BodyCustodian
{
BodyCustodian(const std::shared_ptr<HttpResponse>& response)
: response(response)
, body(response->body()
{ }
std::shared_ptr<HttpResponse> response;
std::string_view body;
};
现在,在您的代码中,您将希望创建其中一个BodyCustodian
保留自己的对象response
(以便主体后面的字符永不过期),并且 abody
是您要返回的实际 string_view。我们构造其中一个,然后使用别名shared_ptr
构造函数创建一个指向(只要保管人还活着就有效body
的元素),它“拥有”保管人。BodyCustodian
std::shared_ptr<std::string_view>, download_data(){
std::shared_ptr<BodyCustodian> custodian = std::make_shared<BodyCustodian>(some_http_client->send_request("some_link"));
return std::shared_ptr<std::string_view>(custodian, &custodian->body);
}
这个共享指针拥有保管人(它使响应保持活动状态),因此body
字符串视图仍然有效。
这种方法确实需要在堆上创建一个小对象(大小约为 6 个指针)。这通常很快,并且不依赖于正文的长度(这是您在复制到 std::string 时担心的问题)。我make_shared
在这里使用以确保创建一个 ~6 指针大小的对象,而不是为 a 分配 ~4 个指针的空间BodyCustodian
,然后为shared_ptr
控制块分配 ~2 个指针的空间。 make_shared
足够聪明,可以一起做。
推荐阅读
- api - 在 Laravel 中使用 Join Query 的未定义模型
- c# - EF:如何获得 GradeId=1 的“Student”的“Grade”实体
- node.js - Layout.jade 文件打开,但 index.jade 在 heroku 中不起作用
- android - 应用程序被杀死时Android中的FCM通知
- php - WordPress源日志系统的解决方案
- c# - Xamarin 表单上的 ActivityIndicator
- php - 获取错误记录已成功更新致命错误:未捕获错误:调用数组上的成员函数 fetch_assoc()
- python - 熊猫中的列到行?
- jq - 使用 jq 在某个键上组合两个对象数组
- reactjs - 在我的 web 服务调用完成之前,如何防止 componentDidMount 运行