当为字符串变量重新分配新值时,用于字符串的内存在哪里?

时间:2019-03-28 02:46:46

标签: c++ memory

说我有这个简单的功能

void foo(string a, string b) {

  std::string a_string = a + a; 
  // reassign a new string to a_string
  a_string = b + b + b;

  // more stuff
}

a+a分配一个新的字符串后,为a_string保留的内存会被释放吗?

C ++内存管理中的新手。我仍在缠着它。

5 个答案:

答案 0 :(得分:1)

据我所知,该字符串只是复制到为a_string分配的内存中,因为它比释放内存和分配新的内存更有效。如果为(a + a)分配的内存小于为a_string分配的内存,则会调整大小。

  

字符串为您管理内存。向字符串添加或删除数据时,您不必分配缓冲区空间。如果添加的数量超出了当前分配缓冲区的容量,字符串将在后台为您重新分配它。

std::string and its automatic memory resizing

答案 1 :(得分:1)

char*是资源管理器类。它拥有基础delete。调用std::string对象的析构函数后,即对象本身超出范围时,分配的内存将被std::string d

重新分配std::string时也会发生相同的情况。如果旧内存对于新字符串来说很小,则将被释放,并有可能被新的堆内存替换。资源管理类(例如b + b + b)有很强的保证,不会泄漏内存(假设符合标准的实现)。

由于您要分配一个临时文件,因此无需进行任何复制(因此也无需重新分配)。相反,我将临时delete的内容移到字符串中。这意味着将临时对象的基础指针复制到现有的字符串对象中,并剥夺该临时对象的所有权。这意味着,临时对象不再拥有该内存,因此在分配后直接调用其析构函数时将不会memcpy。这具有巨大的优势,只需要复制一个指针,而不需要复制整个字符串的std::unique_pointer

其他可以保证的资源管理类包括智能指针(std::shared_pointerstd::vector)和集合(例如std::liststd::map// Wait for the route aliased as 'getAccount' to respond cy.server() cy.route('/accounts/*').as('getAccount') cy.visit('/accounts/123') cy.wait('@getAccount').then((xhr) => { cy.get('#loading-bar').should('not.be.visible') }) ... )。

在现代C ++中,很少需要手动执行这种内存管理。提到的资源管理类涵盖大多数情况,并且除非真正必要并且您确切地知道自己在做什么,否则在几乎所有时间都应优先于手动内存管理。

答案 2 :(得分:1)

取决于是短字符串还是长字符串:

std::string a_string = a + a;
// reassign a new string to a_string
a_string = b + b + b; 

首先,由于保证了 copy elison (c ++ 17),a + a直接构造为a_string。这里没有释放。

然后,如果a_string足够短,则将其分配在堆栈上,而无需其他堆分配。这种短字符串优化(SSO)是由大多数编译器执行的,但不是标准强制要求的。

如果发生SSO,则此操作不会释放a_string中的任何空间,而只会重用它。内存无处可寻:

a_string = b + b + b; 

但是,如果a_string对于SSO来说太长,则此行会释放为该字符串分配的空闲堆空间。

很明显,在检查the declaration of std::string时可以看到内存在哪里:

template< 
   class CharT,
   class Traits = std::char_traits<CharT>,
   class Allocator = std::allocator<CharT>
> class basic_string;

使用std::allocator分配和释放非SSO字符串的内存。该分配器使用newdelete操作符分配和释放内存。通常,他们在后台使用malloc / free。 This is how malloc and free work.

通过运行很容易找出SSO字符串的大小

std::cout << std::string().capacity() << '\n';

对于64位Intel SSO上的clang 8.0.0,最多可包含22个字符的字符串,而对于gcc 8.3,则仅为15个字符。

答案 3 :(得分:0)

不,字符串的工作方式有点像C ++中的向量,因为一旦在内存中保留了空间,除非明确告知这样做,否则它将不会被释放,或者它会通过其最大容量。这是为了避免尽可能地调整大小,因为这样做将意味着分配新的字符数组,复制必要的值并删除旧的数组。通过保留保留的内存,删除字符不需要制作全新的数组,并且仅当字符串的大小不足以容纳您要放入的字符串时才需要重新分配。希望有帮助!

答案 4 :(得分:0)

请注意,您有很多分配,从按值而不是const引用传递的参数开始。

然后a+a,您无法避免。

然后是有趣的部分:

(b + b) + b为2个临时对象创建2个分配。

然后您使用移动分配,将临时a_string替换为:无分配。