【问题标题】:Is it possible to do a swap on return in c++, instead of return by value?是否可以在 C++ 中进行交换,而不是按值返回?
【发布时间】:2016-08-24 04:25:07
【问题描述】:

假设我正在用 C++ 编写一个字符串类(我知道我可以使用该库)。字符串长度是可变的,存储空间在构造函数中动态分配并在析构函数中释放。当main函数调用c=a+ba,b,c是字符串)时,operator+成员函数会创建一个临时对象来存储连接的字符串a+b,将其返回给main函数,然后@调用987654331@成员函数释放原来存储在c中的字符串,并将临时字符串a+b中的数据复制到c,最后临时a+b被销毁。

我想知道是否有办法实现这一点:我希望它交换a+bc 的数据指针,而不是让operator= 将数据从a+b 复制到c ,这样当a+b被破坏时,它会破坏c中的原始数据(这就是我们想要的),而c现在不需要复制a+b的结果。

我知道编写一个 2 参数成员函数 setToStrcat 并调用 c.setToStrcat(a,b) 可以做到这一点。例如,函数可以编码为:

    void String::setToStrcat(const String& a,const String& b){
      String tmp(a.len+b.len); int i,j;
      for(i=0;i<a.len;i++) tmp[i]=a[i];
      for(j=0;j<b.len;i++,j++) tmp[i]=b[j];
      tmp[i]='\0'; this->swap(tmp);
    }

    void String::swap(String& a){
      int n=len; len=a.len; a.len=n;
      char *s=str; str=a.str; a.str=s;
    }

我省略了构造函数(分配len+1 字符型空格)和operator[](返回ith 字符的引用)的定义。 swap函数将*thistmp之间的数据指针和长度变量进行交换,这样当tmp在交换后被破坏时,实际上是原来存储在*this中的数据(String cmain 函数)被破坏。 *this 现在拥有的 (c.str) 是串联的字符串 a+b

我想知道是否有办法将c=a+b 的性能优化到相同的水平。我尝试了c.swap(a+b) 并将a+b 的返回类型更改为String&amp;,但我收到警告(对局部变量的引用)并且GDB 显示临时在交换发生之前被破坏,而我想要另一种方式。

我认为我的问题很笼统。在 C++ 编程中,我们经常需要一个临时对象来存储函数的结果,但是当我们将它分配给主函数中的另一个对象时,我们可以不复制数据而是使用(更快)指针交换吗?有什么巧妙的方法可以做到这一点?

【问题讨论】:

  • 有一个巧妙的方法:) 搜索移动语义,更具体地说:移动构造函数和移动赋值运算符。
  • 还没听说过移动语义? Read up.
  • 你所说的看起来像copy elision。在 C++ 中,它会从 C++17 开始得到保证。
  • 在我的编译器中,如果我执行String c=a+b;,则不会调用operator=(我猜是指针值和长度变量的逐字节复制)。这是复制省略。但如果我执行String c; c=a+b;,则会调用operator=。这是正确的,因为否则 c 中的原始数据(空字符串)不会被释放。我想要的只是一个基于交换的副本(交换a+bc 的数据指针),而不是没有副本(在这种情况下这是不正确的)。

标签: c++ string pointers swap


【解决方案1】:

在 C++11 中,您可以通过编写移动构造函数来做到这一点。语言中添加了右值引用来解决这个确切的问题。

class String {
  ...
  String(String&& s) : str(nullptr) {
    this->swap(s);
  }
  String& operator=(String&& s) {
    this->swap(s);
  }
  ...
  String operator+(String const& other) {
    // (your implementation of concatenation here)
  }
  ...
}

然后这样的代码不会触发额外的复制构造函数或内存分配,它只会将分配的内存从临时(从 operator+ 返回的东西)移动到新对象c

String c = a + b;

【讨论】:

  • String c = a + b; 无论如何都可能执行复制省略
  • 我明白了。临时字符串a+b 是一个右值。要引用它,我应该使用右值引用。 String 类的 operator= 可以进行 2 倍重载,一个采用 const 左值引用并进行深度复制,另一个采用右值引用并进行指针交换。当编译器无法决定调用哪一个时,使用std::move 语义显式转换为右值。还有一个问题要确定:a+bc=a+b 之后仍然被破坏吗?
  • 是的,它被破坏了,但它包含nullptr,所以它不会释放任何东西。
  • 我在c++11中试过了。它就像我想要的那样工作。谢谢!可以将a+bc 交换,并让a+b 的析构函数释放c 的旧数据,或在重载的c 中释放c,它需要右值引用,复制@987654338 的指针@ 到c,并将a+b 的指针设置为nullptr(或0),告诉析构函数不要做任何事情。在 C++ 编程中,“魔法”越少越好。
猜你喜欢
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 2013-05-15
  • 2019-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多