为什么 STL 算法 for_each 调用我的函子的析构函数两次?
我正在尝试 STL 算法,更具体地说是 for_each 函数。 我尝试了一个简单的用例来连接字符串向量。请注意,这可能不是一个好的和/或有效的代码。如果您确实想连接字符串向量,请查看 boost::algorithm::join 函数。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "concatenator.h"
using namespace std;
int main(int argc, char **argv) {
vector<string> list;
list.push_back("hello");
list.push_back("world");
list.push_back("!!!");
Concatenator concatenator;
for_each(list.begin(), list.end(), concatenator);
cout << "result = " << concatenator.getResult() << endl;
}
连接器类作为常规函子实现。
concatenator.h:
#include <string>
class Concatenator {
public:
Concatenator();
virtual ~Concatenator();
void operator()(const std::string s);
std::string getResult();
private:
std::string fResult;
};
concatenator.cpp:
#include "concatenator.h"
#include <iostream>
Concatenator::Concatenator() :
fResult("") {
}
Concatenator::~Concatenator(){
std::cout << "concatenator destructor called " << std::endl;
}
void Concatenator::operator()(const std::string s) {
std::cout << "concat " << s << " to " << this->fResult << std::endl;
this->fResult += " " + s;
}
std::string Concatenator::getResult() {
return this->fResult;
}
如果编译并运行该程序,您将得到以下输出:
concat hello to
连接世界到你好
连起来!!!世界你好
连接器析构函数称为
连接器析构函数称为
结果=
连接器析构函数调用
任何人都可以解释为什么我无法从函子中提取正确的结果以及为什么析构函数被调用如此多次。
I was experimenting with the STL algorithms and more specific with the for_each function.
I tried a simple use case for concatenating a vector of strings. Note that this is probably not a good and/or efficient code. Take a look at the boost::algorithm::join function, if you really want to concatenate a vector of strings.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "concatenator.h"
using namespace std;
int main(int argc, char **argv) {
vector<string> list;
list.push_back("hello");
list.push_back("world");
list.push_back("!!!");
Concatenator concatenator;
for_each(list.begin(), list.end(), concatenator);
cout << "result = " << concatenator.getResult() << endl;
}
The concatenator class is implemented as a regular functor.
concatenator.h:
#include <string>
class Concatenator {
public:
Concatenator();
virtual ~Concatenator();
void operator()(const std::string s);
std::string getResult();
private:
std::string fResult;
};
concatenator.cpp:
#include "concatenator.h"
#include <iostream>
Concatenator::Concatenator() :
fResult("") {
}
Concatenator::~Concatenator(){
std::cout << "concatenator destructor called " << std::endl;
}
void Concatenator::operator()(const std::string s) {
std::cout << "concat " << s << " to " << this->fResult << std::endl;
this->fResult += " " + s;
}
std::string Concatenator::getResult() {
return this->fResult;
}
If you compile and run this program, you get the following output:
concat hello to
concat world to hello
concat !!! to hello world
concatenator destructor called
concatenator destructor called
result =
concatenator destructor called
Can anybody explain why I can't extract the right result from the functor and why the destructor is called so many times.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
std::for_each
按值而非引用获取函子对象。然后它按值返回它。换句话说,您的原始函子对象永远不会被修改。所以你需要这样做:顺便说一句,按值传递必然会创建对象的副本,因此需要额外的析构函数调用。
std::for_each
takes the functor object by value, not by reference. It then returns it by value. In other words, your original functor object never gets modified. So you need to do:Incidentally, pass-by-value necessarily creates a copy of the object, hence the extra destructor calls.
函数对象按值传递给
for_each
并由for_each
按值 返回,因此的三个实例>Concatenator
被创建:Concatenator 连接器创建一个实例;
for_each
并复制它,因为for_each
按值获取for_each
按值返回函子,导致创建另一个副本这三个对象中的每一个都被销毁,因此析构函数被调用三次。
The function object is passed to
for_each
by value and returned byfor_each
by value, so three instances ofConcatenator
get created:Concatenator concatenator;
for_each
and it is copied becausefor_each
takes it by valuefor_each
returns the functor by value, causing another copy to be createdEach of these three objects is destroyed, hence the destructor gets called three times.
当您实现析构函数时,您可能还需要实现复制构造函数和复制赋值运算符。这就是所谓的“三法则”。
一旦正确实现了这两个方法,您就会发现它不会调用析构函数两次,而是创建一个副本并销毁每个副本。
When you implement a destructor, it's likely that you also need to implement a copy constructor and copy assignment operator. This is known as the rule of three.
Once you correctly implement those two methods, then you'll see that it's not calling the destructor twice, but instead it's making a copy and destructing each of the copies.
其他答案已经向您解释了您的情况的问题是将函子对象传递给
for_each
并从for_each
返回按值。然而,虽然
for_each
的声明确实造成了这种行为,但最终的决定是由 C++ 的模板参数推导机制决定的。根据语言规则,在此调用中for_each
模板的第二个参数被推导为Concatenator
而不是Concatenator &
,产生按值传递语义。您可以通过显式指定模板参数来覆盖推导,坚持第二个模板参数的引用类型
这将完全消除复制,并将 pass-by_value 语义替换为 pass-by-reference 语义(还覆盖
for_each
)。它看起来不太优雅,特别是因为函子类型恰好是第二个模板参数,但它是一种解决方法。Other answers already explained to you that the problem in your case is functor object being passed to
for_each
and returned fromfor_each
by value.However, while it is true that the declaration of
for_each
is responsible for this behavior, the final word is said by the template argument deduction mechanism of C++. In accordance with the rules of the language, in this callthe second argument of
for_each
template is deduced asConcatenator
and not asConcatenator &
, resulting in pass-by-value semantics.You can override the deduction by specifying template arguments explicitly, insisting on the reference type for the second template argument
This will completely eliminate the copying and replace the pass-by_value semantics with pass-by-reference semantics (also covering the return value of
for_each
). It doesn't look as elegant, especially because functor type happens to be the second template argument, but it is a workaround.for_each
采用函子按值 并返回它的副本作为返回值,因此修改的不是您的连接器
,而是本地于随后返回的for_each
函数。您应该将代码更改为类似的内容:在
concatenator
中,现在您将拥有修改后的函子。关于析构函数调用:它们在
for_each
返回时开始;第一个是for_each
的参数之一,第二个是for_each
返回的它的副本(被丢弃),第三个是原始连接器对象的,该对象在程序结束时被破坏。for_each
takes the functor by value and returns a copy of it as the return value, so it's not yourconcatenator
that is modified, but the one local to thefor_each
function that is then returned. You should change your code to something like that:In
concatenator
now you'll have your modified functor.About the destructor calls: they begin when
for_each
returns; the first one is the one of the parameter offor_each
, the second one is the one of its copy returned byfor_each
(which is discarded), the third one is the one of the originalconcatenator
object, which is destructed when the program ends.