c++ - C++-字符串复制任务

标签 c++ pointers

在我仍未通过指针方法进行考试的一项准备工作中,我仍处于学习该语言的最开始(我只学习过Java)。

因此,任务是将string s复制多少次以及在何处进行复制。

我认为在t1中,由于指向地址的指针,字符串将被复制。我不确定。另外我也不知道字符串后面的&-符号是什么。

下面是代码:

#include <string>
using namespace std;
string t1(string z) { return z; }
string *t2(string &z) { return &z; }
string& t3(string *z) { return *z; }
string& t4(string& z) { return z; }
string t5(string &z) { return z; }

int main() {
  string s;
  t1(s);
  t2(s);
  t3(&s);
  t4(s);
  t5(s);
  return 0;
}

最佳答案

在开始讨论之前,让我们介绍一下大多数字符串类的一些特殊之处。字符串类通常被实现为一种指向字符串缓冲区的智能指针。这意味着:

std::string s1("testing");
std::string s2;

s2 = s1;


尽管s2是唯一的字符串类,但是在分配s2 = s1之后,它们之间仍然只有一个字符串缓冲区。该缓冲区不被复制,而是以一种只读方式共享。如果对s2中的字符串进行了更改,则此时将创建一个副本,以使两个字符串指向不同的缓冲区。

您的问题可能与缓冲区本身无关,而与操作这些缓冲区的字符串对象有关,但是在涉及复制性能的字符串(以及类似原因的std :: shared_ptr)的情况下,它与切线相关。复制std :: string类通常比复制基础缓冲区要少得多。

就是说,关于代码示例,还有一点值得我们解决,那就是对这些函数的返回值进行处理(部分原因是您询问了其中两个字符串后面的&表示什么)。

重复并略有扩展:

#include <string>
using namespace std;
string t1(string z) { return z; }
string *t2(string &z) { return &z; }
string& t3(string *z) { return *z; }
string& t4(string& z) { return z; }
string t5(string &z) { return z; }

int main() {
  string s; string x; string *xp
  x  = t1(s);
  xp = t2(s);
  x  = t3(&s);
  x  = t4(s);
  x  = t5(s);
  return 0;
}


现在,暂时扩展函数t1很重要。有理论和有实际结果,在所有现代C ++编译器中都有所不同。在考试中,我希望您会回答纯理论,而忽略了此处使用的删节副本。考虑x = t1(s),理论上s被复制为函数的参数,此时函数内的z是调用者s的副本。返回值是按值计算的,因此理论上会创建第二个副本以返回。然后,理论上,当分配了x时执行另一个副本。现在,如果您在调试器中进行跟踪,那也可能是您所看到的。但是,除最幼稚的编译器外,所有这些副本都将被删除,以使x将收到s的副本,就像编写了x = s一样(大多数编译器会检查该文字代码,意识到什么也没做,并且发出一个只返回就什么都不做的程序)。

现在,关于x = t2(s);参数是对字符串的引用(这些内容是从右向左解释的,因此即使大多数人说“字符串引用”,也请考虑对字符串的引用。这意味着该函数没有使用任何副本,是调用者的s。此函数返回该字符串的地址,一个指针,这意味着没有s的副本-最多可以说返回了该指针的副本,这与编写xp = &s;相同

x = t3(&s)中,我们有一个奇怪的情况。该函数接受一个指针,该指针要求&s使用s的地址来提供该指针,因此在函数调用时不会复制s。该函数返回对字符串的引用(从前到右,从前到右读取,尽管有些人可能会说一个字符串引用)。由于这是对指针的取消引用,因此结果只是通过其地址引用s,并且在返回中不进行任何复制。返回是参考,这一事实进一步证明了这一点。引用被实现为指针。这是一种特殊的指针,但是在幕后,它是一个指针-不进行复制。但是,由于x是唯一的对象,因此在为其分配x时,会从该引用中分配一个副本。它解决与编写x = s;相同的问题

此功能支持的其他用例还需要单独考虑:

string xr( t3( &s ) );


在这种情况下,引用用于初始化xr(从t3返回的引用)。与string xr( s );相似。到目前为止,还没有任何启示。但是,请考虑将返回的字符串与t2和t1进行比较。

t1(s).length();
t2(s)->length();
t3(&s).length();


在这里,每个函数的返回用于调用字符串的成员。用t1进行的调用已将s复制到函数中,然后再次复制以返回临时字符串,然后将该临时字符串销毁(将调用析构函数),这是您在查询中并未真正解决的问题。

但是,使用t2和t3进行的调用实际上是使用s进行调用,而没有暗示任何副本。但是,在t2情况下,调用是通过指针进行的。 t2情况类似于(奇数)写入(&s)-> length(),而t3情况与写入s.length()相同。

T4与t3完全相同,只是调用方式不同,其含义与nullptr可能传递给t3的可能性(导致取消引用时崩溃)有关,t4不会发生这种情况。

T5与t4(和t3)的不同之处仅在于,由于按值返回而隐含了副本。返回的内容类似于t1,其操作方式类似于t1,并且与t1的不同之处仅在于,它暗示t5不会为函数主体创建用于操作的副本,而只是为返回创建副本。

假设您提供了示例代码,请在对t5的调用之后附加main:

string a, b;

// t1 is like having written:

a = s;
b = a;
x = b;

// t5 is like having written:

b = s;
x = b;


意思是,t1的第一个副本被删除,因为t5引用了值而不是值。

在现代C ++中,通常在理论上忽略t1或t4,t5之类的性能含义。我们更关心为什么使用引用而不是副本,因为使用引用的副作用是,对函数t5中的字符串所做的更改是对调用方的s进行的,而副本则隐含在t1和因此,呼叫者的不变。这是您问题的重要组成部分。

理论上总是会在副本中隐含副本的情况下制作副本,如上所述,但是实际上,由于优化,副本会被删除(避免)。例如,在t1的情况下,文字代码将所有隐含的副本都删除了-不会执行任何副本。但是,如果在t1的函数体内对z进行了更改,则情况会发生变化。如果对t1进行了更改,则编译器会意识到,除非进行复制,否则更改z的副作用将更改s,这意味着将创建t1的按值传递参数所隐含的一个副本,以避免这种副作用,但仍然忽略按值返回所隐含的副本。

关于c++ - C++-字符串复制任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32780178/

相关文章:

c++ - QML 组件和 QML 对象类型有什么区别?

c++ - 不知道这个指针可能指向什么并且无法解释结果

C++ 错误,类指针作为另一个类中的数据成员

c++ - C++ VIRTUAL 函数如何不冗余?

c++ - C++ 中复杂的 for 和 if 条件等价于 Scala

C++ 模板静态成员构造函数未被调用

c++如何将特定行从一个文件复制到另一个文件?

c++ - 澄清列表和删除元素

c++ - 指向连续内存的指针

c++ - C++调试中的指针