c++将实例复制构造到映射中
下面是一个带有简单类 Foo 的代码,该类已实例化,然后插入到地图中。 我不明白将 foo 插入 fooMap 时如何调用复制构造函数。
#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <iostream>
using namespace std;
class Foo {
public:
//default constructor
Foo() {
name_ = string("Undefined") + suffix();
cout << "Constructing Foo '"<< name_ << "'" << endl;
};
//constructor with arg
Foo(const char* name): name_(name) {
cout << "Constructing Foo '" << name << "'" << endl;
};
//default const copy-constructor
Foo(const Foo &foo) {
name_ = foo.get_name() + suffix();
cout << "Copying const Foo '" << foo.get_name() << "' into Foo '" << name_ << "'" << endl;
}
//default destructor
~Foo() {
cout << "Destroying Foo '" << name_ << "'" << endl;
}
//getting name
const string get_name() const {
return name_;
};
//setting name
void set_name(string new_name){
name_ = new_name;
}
//suffix for name
string suffix() {
static int cmp=0;
char ch[2];
sprintf(ch,"%d",cmp++);
return string(ch);
}
private:
string name_;
};
int main() {
typedef map<string, Foo> FooMapType;
FooMapType fooMap;
cout << "1:\n";
Foo foo("bar");
cout << "\n2:\n";
fooMap["bar"] = foo;
cout << "\n3:\n";
cout << fooMap["bar"].get_name() << endl;
foo.set_name("baz");
cout << "\n4:\n";
输出是:
1:
Constructing Foo 'bar'
2:
Constructing Foo 'Undefined0'
Copying const Foo 'Undefined0' into Foo 'Undefined01'
Copying const Foo 'Undefined01' into Foo 'Undefined012'
Destroying Foo 'Undefined01'
Destroying Foo 'Undefined0'
3:
bar
4:
Destroying Foo 'baz'
Destroying Foo 'bar'
但我期望 foo 复制构造函数被调用,导致输出:
Copying const Foo 'bar' into Foo 'Undefined0'
Here is a code with a simple class Foo that in instanciated and then inserted into a map.
I don't understand how the copy-constructor is called when inserting foo into fooMap.
#include <stdlib.h>
#include <stdio.h>
#include <map>
#include <iostream>
using namespace std;
class Foo {
public:
//default constructor
Foo() {
name_ = string("Undefined") + suffix();
cout << "Constructing Foo '"<< name_ << "'" << endl;
};
//constructor with arg
Foo(const char* name): name_(name) {
cout << "Constructing Foo '" << name << "'" << endl;
};
//default const copy-constructor
Foo(const Foo &foo) {
name_ = foo.get_name() + suffix();
cout << "Copying const Foo '" << foo.get_name() << "' into Foo '" << name_ << "'" << endl;
}
//default destructor
~Foo() {
cout << "Destroying Foo '" << name_ << "'" << endl;
}
//getting name
const string get_name() const {
return name_;
};
//setting name
void set_name(string new_name){
name_ = new_name;
}
//suffix for name
string suffix() {
static int cmp=0;
char ch[2];
sprintf(ch,"%d",cmp++);
return string(ch);
}
private:
string name_;
};
int main() {
typedef map<string, Foo> FooMapType;
FooMapType fooMap;
cout << "1:\n";
Foo foo("bar");
cout << "\n2:\n";
fooMap["bar"] = foo;
cout << "\n3:\n";
cout << fooMap["bar"].get_name() << endl;
foo.set_name("baz");
cout << "\n4:\n";
The output is:
1:
Constructing Foo 'bar'
2:
Constructing Foo 'Undefined0'
Copying const Foo 'Undefined0' into Foo 'Undefined01'
Copying const Foo 'Undefined01' into Foo 'Undefined012'
Destroying Foo 'Undefined01'
Destroying Foo 'Undefined0'
3:
bar
4:
Destroying Foo 'baz'
Destroying Foo 'bar'
But I expected the foo copy-constructor to be called, causing the output:
Copying const Foo 'bar' into Foo 'Undefined0'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您正在执行赋值操作,而不是在
fooMap["bar"] = foo;
行中进行复制构造,覆盖Foo &operator=(const Foo&)
来查看发生的情况。默认构造是创建您要进行分配的初始项目。
您看到的副本可能是地图实现的内部 - 将其放入树中的适当位置。数量超出了我的预期。
You're doing assignment, not copy construction in the line
fooMap["bar"] = foo;
OverrideFoo &operator=(const Foo&)
to see that happening.The default construction is the creation of the initial item you would do the assignment into.
The copies you're seeing are probably internal to the map implementation - putting it into the appropriate place in the tree. There are more than I would have expected.
map::operator[]
返回对值成员的引用,因此以下语句:可以解释为:
在 (1)
map::operator[]
中执行并且在 (2) Foo::operator= 中。如果映射中不存在给定的键,
map::operator[]
会创建一个新元素 - 一对由提供的键和使用默认构造函数构造的值对象组成。 (在您的示例中,键类型为std::string
,值类型为Foo
)。如果给定的键存在,它只返回对值对象的引用。第 (2) 行通过其引用更改值。
如果你看一下
map::operator[]
的实现(我在这里提供了微软的一个,来自 VS2010 附带的 STL 库),你可以看到它调用了map::插入()
在引擎盖下,为其提供临时对象
value_type
,该对象再次从临时对象mapped_type
创建:value_type
基本上是我上面提到的对和在您的情况下,mapped_type 是
Foo
。mapped_type()
通过调用其默认构造函数来创建临时对象。这与输出中的第二个默认构造函数匹配(第一个构造函数在创建局部变量foo
时调用)。value_type
的构造函数使用mapped_type
的复制构造函数来创建其值成员的实例。这与Foo
的复制构造函数的第一次调用相匹配。日志中还有此构造函数的另一个调用,为了找到其来源,我们需要更深入地研究map::operator[]
...实际上是insert
它调用的方法:Map 被实现为一棵树,我们可以看到内部
_Insert
方法插入了一个新的树节点实例,该实例是使用_Buynode()
创建的:树节点封装配对对象 - 地图的元素,所以它是创建涉及创建我们的对的另一个副本 -
_Val
,这是对Foo
的复制构造函数进行另一次调用的地方。map::operator[]
调用创建了两个包含Foo
的临时对象,并且在返回时这些对象被销毁,因此您可以在输出中看到两个析构函数调用。如果您实现 Foo::operator= 并在其中添加跟踪,您将能够看到该方法也被调用(第 (2) 行)。
如果您稍后将相同的键映射到其他某个
Foo
对象,则只会执行Foo::operator=
,因为该键的映射元素已经创建,并且只有值是通过其引用改变。map::operator[]
returns a reference to a value member so the following statement:can be interpreted as:
In (1)
map::operator[]
is executed and in (2)Foo::operator=
.If given key does not exist in the map,
map::operator[]
creates a new element - a pair made of the provided key and a value object constructed with default constructor. (Key type isstd::string
and value type isFoo
in your example). If given key exists, it just returns a reference to the value object.Line (2) changes value through its reference.
If you have a look at the implementation of
map::operator[]
(I am providing Microsoft's one here, from STL library that comes with VS2010) you can see that it callsmap::insert()
under the bonnet, providing it temporary object
value_type
which is again created from temporary objectmapped_type
:value_type
is basically pair I mentioned above andmapped_type
isFoo
in your case.mapped_type()
creates temporary object by calling its default constructor. This matches the second default constructor in your output (the first one is called when local variablefoo
is created).value_type
's constructor usesmapped_type
's copy constructor in order to create its instance of value member. This matches the first call ofFoo
's copy constructor. There is another call of this constructor in your log and in order to find its source we need to dig a bit deeper intomap::operator[]
...actually intoinsert
method it calls:Map is implemented as a tree and we can see that inner
_Insert
method inserts a new instance of tree node, created with_Buynode()
:Tree node encapsulates pair object - map's element so it's creation involves creation of another copy of our pair -
_Val
and this is the point where another call ofFoo
's copy constructor takes place.map::operator[]
call created two temporary objects containingFoo
and on return these objects were destroyed so therefore you can see two destructor calls in your output.If you implement
Foo::operator=
and put a trace inside it, you would be able to see that this method is called as well (line (2)).If you later map the same key to some other
Foo
object, you would have onlyFoo::operator=
executed because map's element for that key is already created and only value is changed through its reference.这是个好问题,我也有同样的行为。我们(也许是全部?)STL 实现的细节创建默认对象,然后使用另一种方法来复制数据。
退后一步,可以看出,一旦调用了 insert(或者在您的情况下是operator[])方法,原始对象的副本确实存在于映射中,这很可能就是全部标准承诺。
A good question, I get the same behaviour. The specifics of our (and maybe all?) STL implementations create default objects then use another method to copy the data across.
Standing back, it can be seen that once the insert (or in your case operator[]) method has been called a copy of the original object does exist in the map, and that may well be all that the standard promises.
fooMap["bar"] = foo
产生默认构造、两个副本和一个赋值。需要默认构造,因为键
"bar"
没有 string-foo 对。第一个副本是由键
"bar"
和从刚刚创建的默认构造foo
复制的值构造而成的对。当该对被复制到地图中时,就会产生第二个副本。
然后,赋值将映射中的值赋给
foo
。在 C++11 中使用 R-Value 语义的 STL 实现可以提高效率。
fooMap["bar"] = foo
results in a default construction, two copies and an assignment.The default construction is required as there is no string-foo pair for the key
"bar"
.The first copy comes about as a pair is constructed with key
"bar"
and value copied from the default constructedfoo
that's just been made.The second copy comes about as the pair is then copied into the map.
The assignment then assigns the value in the map to
foo
.An STL implementation using R-Value semantics in C++11 could make this much more efficient.