我创建一个对象,例如:
std::string s{"string"};
我想把这个对象添加到一个数据结构中(比如std::vector),而不占用两个对象的内存空间。
当我这样做时:
std::vector<std::string> vec;
vec.emplace_back(std::move(s));
std::cout<<"s = "<<s<<"\n";
std::cout<<"vec[0] = "<<vec[0]<<"\n";
s = "different string";
std::cout<<"\ns = "<<s<<"\n";
std::cout<<"vec[0] = "<<vec[0]<<"\n";
我得到这个输出:
s =
vec[0] = string
s = different string
vec[0] = string
所以是的,它“擦除”了原始字符串中的数据,但是如果我能够给它赋值,这意味着对象仍然存在,如果对象仍然存在,这不意味着程序在内存中为两个对象和它们的所有成员变量保留了足够的空间吗?
即使我把字符串创建为右值,如下图所示,我相信它会调用构造函数,移动它,然后调用析构函数,这意味着内存中有两个对象。
vec.emplace_back("string");
基本上,我想知道,什么是最有效的方法,让一个对象到一个数据结构?
1条答案
按热度按时间rqqzpn5f1#
你说的有对也有错。
由于您使用了
string
,让我们考虑一个简单的string实现:因此,字符串对象本身只存储字符串的当前长度、分配的空间量以及指向字符串本身的存储的指针。
当你执行类似
a = std::move(b);
的操作时,它会将这些大小和指针从b
复制到a
,然后设置b
中的值以指示它是空的。所以你暂时有两个字符串 * 对象 *,但你 * 没有 * 字符串数据本身的副本。因此,如果你有一个字符串(比如说)20兆字节长,这个赋值在任何时候都不需要40兆字节的内存。字符串对象可能占用24个字节,在这种情况下,在赋值之前,你有20兆字节+ 24字节,在赋值期间,你有20兆字节+ 48字节,而在b
被销毁后,你会回到20兆字节+ 24字节。不过还有一个问题。大多数
std::string
的实现都有所谓的“小字符串优化”。很多字符串都很短,用24个字节来跟踪它+堆上的一个内存块来存储字符串本身并不是很有效。典型的当前处理器使用64位类型的指针,但它实际上最多只支持42位寻址(左右--因处理器而异)。
在大多数情况下,我们可以在该指针的一个字节中使用(例如)一个特殊值来表示它不是真正的指针。相反,我们做一些类似的事情:
从那里,我们查看第一个字节,看看它是否是表示内部字符串的“魔法”值。如果是,那么我们将其视为
internalString
,将数据存储在字符串对象本身中。在本例中,
std::move
并不能真正完成很多任务。将数据从一个字符串移动到另一个字符串仍然需要将数据从一个字符串复制到另一个字符串,在移动过程中,我们有两个字符串对象占用一个字符串对象的两倍空间。但它只发生在弦很小的时候,所以我们通常不太在意。免责声明
为了避免我之前没有弄清楚,我在这里简化了很多东西。可能有点太多了。但希望足够好,这样就很容易掌握手头的要点。