1>>哪种选项更好(从性能考虑),为什么?
1.
string a;
a = "hello!";
或
2.
string *a;
a = new string("hello!");
...
delete(a);
2>>
string a;
a = "less";
a = "moreeeeeee";
当一个更大的字符串复制到一个较小的字符串中时,C++如何处理内存管理?C ++字符串是否可变?
string a;
a = "hello!";
或
2.
string *a;
a = new string("hello!");
...
delete(a);
2>>
string a;
a = "less";
a = "moreeeeeee";
几乎从来不需要或者说是不可取的去这样做
string * s = new string("hello");
毕竟,你(几乎)从不会说:
int * i = new int(42);
你应该使用以下说法
string s( "hello" );
或者
string s = "hello";
是的,C++字符串是可变的。
string a;
a = "hello!";
首先,您需要初始化一个包含空字符串的a变量(将长度设置为0,并执行一两个其他操作)。然后,您会分配一个新值,覆盖之前已经设置的长度值。它可能还需要检查当前缓冲区的大小,以及是否需要分配更多内存。
string *a;
a = new string("hello!");
...
delete(a);
调用new需要操作系统和内存分配器找到一个空闲的内存块,这是很慢的。然后您立即对其进行初始化,因此您不会重复分配任何内容或要求缓冲区重新调整大小,就像在第一个版本中一样。 然后发生了一些不好的事情,您忘记调用delete,就会有内存泄漏,此外,字符串分配非常缓慢。所以这很糟糕。
string a;
a = "less";
a = "moreeeeeee";
和第一种情况一样,您首先将a初始化为空字符串。然后您分配一个新字符串,接着又分配了另一个新字符串。每个字符串的分配 可能 需要调用new来分配更多内存。每行代码还需要分配长度和可能需要分配其他内部变量。
通常,您可以像这样分配它:
string a = "hello";
一行代码,执行一次初始化,而不是先进行默认初始化,然后再分配所需的值。
这也可以最小化错误,因为你的程序中没有无意义的空字符串。如果字符串存在,则包含你想要的值。
关于内存管理,请查阅 RAII。简单来说,字符串在内部调用 new/delete 来调整其缓冲区大小。这意味着你 永远不需要 使用 new 来分配字符串。字符串对象具有固定大小,并设计为在堆栈上分配,因此当它超出范围时,析构函数会 自动 调用。析构函数确保释放任何已分配的内存。这样,你就不必在用户代码中使用 new/delete,这意味着你不会泄漏内存。
string a = "Hello";
等等?这避免了默认构造,从语义上讲更有意义。仅仅为了在堆上分配一个字符串而创建指向字符串的指针是没有意义的,也就是说,你的情况2没有意义,并且效率略低。
至于你最后的问题,是的,在C++中字符串是可变的,除非声明为const
。
string a;
a = "hello!";
2个操作:调用默认构造函数std:string(),然后调用operator::=运算符。
string *a; a = new string("hello!"); ... delete(a);
只有一个操作:调用构造函数std:string(const char*),但您不应忘记释放指针。
那么,对于以下情况呢? string a("hello");
a
超出范围时,类实例占用的内存将被释放。char*
常量赋给一个字符串时,将realloc
内存以适应新数据。string :: capacity()
来查看分配了多少内存。string a("hello")
时,内存会在构造函数中分配。如果您查看STL字符串类的文档(我相信SGI文档符合规范),许多方法列出了复杂度保证。我认为许多复杂度保证故意保持模糊,以允许不同的实现。我认为一些实现实际上使用了复制修改的方法,这样将一个字符串赋值给另一个字符串是一个常数时间操作,但当您尝试修改其中一个实例时,可能会产生意外的代价。不确定在现代STL中是否仍然如此。
您还应该检查capacity()
函数,它将告诉您可以在给定的字符串实例中放置的最大长度字符串,然后它将被强制重新分配内存。如果您知道将来要在变量中存储大字符串,则还可以使用reserve()
来导致重新分配到特定数量。
正如其他人所说,就您的示例而言,您应该真正青睐初始化而不是其他方法,以避免创建临时对象。
在堆内直接创建字符串通常不是一个好主意,就像创建基本类型一样。这并不值得,因为对象可以轻松地留在堆栈上,并且它具有所有所需的复制构造函数和赋值运算符,以便进行高效的复制。
std:string 本身在堆中有一个缓冲区,可能由多个字符串共享,具体取决于实现。
例如,在 Microsoft 的 STL 实现中,您可以这样做:
string a = "Hello!";
string b = a;
并且直到您更改它,两个字符串将共享相同的缓冲区:
a = "Something else!";
这就是为什么将 c_str() 存储以供以后使用非常糟糕;c_str() 仅保证在对该字符串对象进行另一次调用之前有效。
这导致非常恶劣的并发错误,如果您在多线程应用程序中使用它们,则需要通过定义关闭此共享功能。
很可能
string a("hello!");
是最快的。
string a;
a = "less";
a = "moreeeeeee";
这会在堆栈上放置一个内存块并将其命名为a,然后调用构造函数并将a初始化为空字符串。编译器将“less”和“moreeeeeee”的字节存储在exe的.rdata部分中(我认为是这样)。字符串a将具有一些字段,例如长度字段和char *(我大幅简化了)。当您将“less”分配给a时,将调用operator =()方法。它动态分配内存以存储输入值,然后将其复制。当您稍后将“moreeeeeee”分配给a时,再次调用operator =()方法,并在必要时重新分配足够的内存来容纳新值,然后将其复制到内部缓冲区中。
当字符串a的作用域退出时,将调用字符串析构函数,并释放动态分配用于保存实际字符的内存。然后递减堆栈指针,并且保存a的内存不再处于堆栈上。