如何实现一个创建新对象并返回对它的引用的C++方法

How to implement a C++ method that creates a new object, and returns a reference to it

本文关键字:返回 方法 引用 对象 C++ 新对象 实现 何实现 创建 一个      更新时间:2023-10-16

我有一个带有以下方法的C++AuthenticatingProxy类实例。此方法创建一个Response对象,然后用状态更新该对象,然后返回。由于响应对象的内部原因,它不能被复制(即,我不能简单地按值返回)。

const Response& AuthenticatingProxy::Get(const std::string& host,
                                  const std::string& path,
                                  const http_headers& headers)
{
  static Response response;
  // do the HTTP call, and set response's state here
  return response;
}

AuthenticatingProxy类中的此方法可能会发生许多调用,因此在此处设置静态变量并不理想。有人能提出一个解决方案,返回一个在函数退出时没有被破坏的引用吗?谢谢

在我的研究中,最接近的是最佳实践页面,其中提到"通过引用而非价值返回,在那里创建了一个大对象",但我还没有找到一个例子!所有的例子都是针对传入然后返回的引用,或者针对int&样式引用—而不是在函数本身中创建对象实例的引用。

我认为解决方案可能存在于将函数返回响应作为值,但在调用代码中使用const变量来捕获它。不过,不确定这是否只适用于int-我发现的所有示例都使用基本类型。非常感谢您的帮助。

由于响应对象的内部原因,它不能被复制(即,我不能简单地按值返回)。

您正在描述一个在C++03中存在但已不存在的问题。

以老爱的std::fstream为例
基本上,在*内部,它包含一个文件描述符/句柄,用于从文件中读取/向文件中写入
由于C++的性质和设计,fstream析构函数关闭了该文件句柄,以清理对象并防止句柄泄漏。

由于fstream对象的内部原因,无法按值返回。通过值返回它意味着以某种方式防止析构函数关闭该文件句柄,或者使复制构造函数以某种方式复制该句柄。即使不是不可能,也很难获得跨平台解决方案。更不用说复制内部缓冲区是完全错误的。

因此,在C++11之前,您不能按值返回fstream
然后发明了移动语义

在C++11中,您可以移动对象,而不是复制它。在fstream示例中,move构造函数浅层复制文件句柄,同时使原始文件句柄指针无效。原始对象析构函数将检查原始文件句柄,查看它是否无效并跳过关闭它

这是解决你问题的惯用方法。虽然你不能复制对象,但你肯定可以为它实现移动语义。对象的内部将被移动到返回值,而原始对象在某种程度上保持"空"。

在这个SO答案中可以找到对移动语义的很好解释:什么是移动语义?

如果该解决方案也不可行,请在动态内存存储("堆")中声明对象,并通过一些智能指针返回它。

如何实现一个创建新对象并返回参考

即使可以,也不要做。

*是的,文件句柄可以存储在streambuff对象中,我简单地描述了这个问题。

C++中有三个存储类。静态、自动和动态。

退出作用域时,自动对象将被销毁,因此不能返回对本地自动变量的引用。

您可以返回对静态的引用,但正如您所说,它不适合您。

剩下的唯一选项是动态。然而,动态对象必须以某种方式销毁,并且当您返回对动态创建的对象的引用时,调用方不清楚它是否应该处理它的销毁。因此,这在技术上是可能的,但内存管理没有很好的解决方案。此外,动态分配可能代价高昂。

当您坚持返回参考资料时,不会。但是,如果您返回std::unique_ptr,这个问题就会消失。

那么,让我们重新审视自动变量的选项。我说过,您不能返回对局部自动变量的引用。从更高范围返回对自动变量的引用是完全可以的。如何访问这样的变量?当然要使用参考资料!

const Response& AuthenticatingProxy::Get(const std::string& host,
                                  const std::string& path,
                                  const http_headers& headers,
                                  Response& response)
{
  // do the HTTP call, and set response's state here
  return response; // might as well return void because caller already has a reference
}

当动态分配过于昂贵并且编译器不支持移动语义时,这是一个典型的解决方案。在这个解决方案中,函数中实际上没有创建任何对象。它只是为调用者创建的对象设置数据。对象实际上可以有任何存储类型,这无关紧要。


然而,最好的解决方案是按价值回报。你说Response是不可复制的。但由于C++11,只要类型是可移动的,这并不妨碍按值返回。大多数编译器甚至忽略了这一举措。

Response response;
// do stuff to response
return response;

感谢您的提示,我找到了一个答案。。。

(尽管正如其他人所提到的,它给调用者留下了内存管理的问题。这可以满足我的需求。)

本页对此进行了描述:http://www.codeproject.com/Articles/823658/Using-auto-for-declaring-variables-of-move-only-ty

这是我的新方法声明:-

const Response* AuthenticatingProxy::Get(const std::string& host,
                                  const std::string& path,
                                  const http_headers& headers)
{
  auto && response = new Response;
  // do stuff to response
  return std::move(response);
}

因此,我返回一个指针,但以上述方式使用std::move和auto会返回一个可以在调用代码中管理的指针。

编辑:-

好的,所以根据反馈,我现在在AuthenticatingProxy.cpp:-中有这个

std::unique_ptr<Response> AuthenticatingProxy::Get(const std::string& host,
                                  const std::string& path,
                                  const http_headers& headers)
{
  Response* response = new Response;
  // do call and fill out the response
  return std::unique_ptr<Response>(response);
}

我的中间连接类正在这样做:-

std::unique_ptr<Response> Connection::getDocument(const std::string& uri) {
  return Connection::_proxy.Get(_serverUrl, "/v1/documents?uri=" + uri); 
}

注意:我不得不从代理和连接类中删除constness,否则我会遇到使用已删除的副本构造函数的编译问题)

我的客户端代码(main)是这样做的:-

    // MUST keep local reference to unique_ptr for this to work!!!
    const std::unique_ptr<Response> rp = connection->getDocument(uri); 
    Response* response = rp.get();
    ...
    std::cout << "This is JSON doc " << uri << ": " << std::endl << response->Json() << std::endl;

这似乎很好用,并且包含了关于这个线程的所有建议。现在这样正确吗?如果是的话,感谢大家的帮助!