在结构的STL地图,为什么“[]”操作符事业结构的析构函数被调用2倍多?
-
26-09-2019 - |
题
我已经创建了一个简单的测试情况下表现出奇怪的行为我已经在一个较大的代码库我正在注意到。这个测试案例如下。我依靠STL Map的“[]”操作符在地图上这样的结构来创建一个指向一个结构。在下面的试验情况下,线...
TestStruct *thisTestStruct = &testStructMap["test"];
...让我的指针(并创建地图的新条目)。我注意到奇怪的是,此行不仅会导致创建在地图上的新条目(因为“[]”运算符),但由于某种原因,导致结构的析构函数被调用两个额外的时间。我显然失去了一些东西 - 任何帮助深表感谢! 谢谢!
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct TestStruct;
int main (int argc, char * const argv[]) {
map<string, TestStruct> testStructMap;
std::cout << "Marker One\n";
//why does this line cause "~TestStruct()" to be invoked twice?
TestStruct *thisTestStruct = &testStructMap["test"];
std::cout << "Marker Two\n";
return 0;
}
struct TestStruct{
TestStruct(){
std::cout << "TestStruct Constructor!\n";
}
~TestStruct(){
std::cout << "TestStruct Destructor!\n";
}
};
上述输出代码下面...
/*
Marker One
TestStruct Constructor! //makes sense
TestStruct Destructor! //<---why?
TestStruct Destructor! //<---god why?
Marker Two
TestStruct Destructor! //makes sense
*/
...但我不明白是什么原因导致TestStruct的析构函数的前两个调用? (我认为最后的析构函数调用很有意义,因为testStructMap被的范围外出。)
解决方案
std::map<>::operator[]
的功能相当于
(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
表达,如在语言规范中指定。这一点,因为你可以看到,包括默认构造型T
的临时对象,将其复制到std::pair
对象,这是后来复制(再次)进入地图的新元素(假设它是不存在的话)。显然,这会产生一些中间T
对象。这些中间对象的破坏是你在实验中观察到的东西。你错过了他们的建筑,因为你不生成类的拷贝构造函数的任何反馈。
中间对象的确切数量可能依赖于编译器的优化能力,所以结果可能会有所不同。
其他提示
您已经有些看不见的副本正在取得:
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct TestStruct;
int main (int argc, char * const argv[]) {
map<string, TestStruct> testStructMap;
std::cout << "Marker One\n";
//why does this line cause "~TestStruct()" to be invoked twice?
TestStruct *thisTestStruct = &testStructMap["test"];
std::cout << "Marker Two\n";
return 0;
}
struct TestStruct{
TestStruct(){
std::cout << "TestStruct Constructor!\n";
}
TestStruct( TestStruct const& other) {
std::cout << "TestStruct copy Constructor!\n";
}
TestStruct& operator=( TestStruct const& rhs) {
std::cout << "TestStruct copy assignment!\n";
}
~TestStruct(){
std::cout << "TestStruct Destructor!\n";
}
};
结果:
Marker One
TestStruct Constructor!
TestStruct copy Constructor!
TestStruct copy Constructor!
TestStruct Destructor!
TestStruct Destructor!
Marker Two
TestStruct Destructor!
以下添加到TestStruct的接口:
TestStruct(const TestStruct& other) {
std::cout << "TestStruct Copy Constructor!\n";
}
您的两个神秘的析构函数调用可能与拷贝构造函数调用正在进行的std::map
内的某处配对。例如,它是可以想象的operator[]
默认构造一个临时TestStruct
对象,然后将其复制到构建在地图中的正确位置。有两个析构函数调用(因此可能有两个拷贝构造函数调用)的原因是实现特定的,将取决于你的编译器和标准库的实现。
operator[]
插入到map
如果没有已经存在的元件。
你所缺少的是在你的TestStruct
,这是集装箱看家期间使用的编译器提供的拷贝构造函数的输出。补充一点输出,它都应该更有意义。
编辑:安德烈的回答促使我去看看在微软VC ++ 10的<map>
,这是一件好事,你也可以做到遵循此通过其所有的血腥细节的来源。你可以看到insert()
呼叫他提到。
mapped_type& operator[](const key_type& _Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = this->lower_bound(_Keyval);
if (_Where == this->end()
|| this->comp(_Keyval, this->_Key(_Where._Mynode())))
_Where = this->insert(_Where,
value_type(_Keyval, mapped_type()));
return ((*_Where).second);
}
这样的教训是 - 不要把结构中的地图,如果你关心自己的生命周期。使用指针,甚至更好shared_ptrs他们
可以检查出来通过这个更加简单代码。
#include <iostream>
#include <map>
using namespace std;
class AA
{
public:
AA() { cout << "default const" << endl; }
AA(int a):x(a) { cout << "user const" << endl; }
AA(const AA& a) { cout << "default copy const" << endl; }
~AA() { cout << "dest" << endl; }
private:
int x;
};
int main ()
{
AA o1(1);
std::map<char,AA> mymap;
mymap['x']=o1; // (1)
return 0;
}
在以下结果表明,(1)线路编码上述品牌(1个默认常数)和(2默认复制常数)的呼叫。
user const
default const // here
default copy const // here
default copy const // here
dest
dest
dest
dest