1

我正在试验 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;
}

连接器类被实现为常规函子。

连接器.h:

#include <string>

class Concatenator {
    public:
        Concatenator();

        virtual ~Concatenator();

        void operator()(const std::string s);

        std::string getResult();
    private:
        std::string fResult;
};

连接器.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
concat world to hello
concat !!! to hello world
concatenator destructor called
concatenator destructor called
result =
concatenator destructor called

谁能解释为什么我不能从仿函数中提取正确的结果以及为什么多次调用析构函数。

4

5 回答 5

4

std::for_each通过值而不是引用获取函子对象。然后它按值返回它。换句话说,您的原始仿函数对象永远不会被修改。所以你需要这样做:

concatenator = for_each(list.begin(), list.end(), concatenator);

顺便说一句,按值传递必然会创建对象的副本,因此会调用额外的析构函数。

于 2010-11-17T00:05:52.120 回答
3

函数对象按值传递,for_each 返回,因此创建了三个get实例:for_each Concatenator

  1. 您使用创建一个实例Concatenator concatenator;
  2. 您将此对象传递给for_each它并被复制,因为for_each它是按值获取的
  3. for_each按值返回函子,导致创建另一个副本

这三个对象中的每一个都被销毁,因此析构函数被调用了三次。

于 2010-11-17T00:06:25.867 回答
1

其他答案已经向您解释过,您的问题是仿函数对象被传递给for_each和从for_each value返回。

然而,虽然声明for_each确实对这种行为负责,但最终还是由 C++ 的模板参数推导机制说了算。按照语言的规则,在这个调用中

for_each(list.begin(), list.end(), concatenator);

模板的第二个参数for_each被推导为 asConcatenator而不是 as Concatenator &,从而产生按值传递的语义。

您可以通过显式指定模板参数来覆盖推导,坚持第二个模板参数的引用类型

for_each<vector<string>::iterator, Concatenator &>(ist.begin(), list.end(),
    concatenator);

这将完全消除复制并将 pass-by_value 语义替换为 pass-by-reference 语义(也涵盖 的返回值for_each)。它看起来不那么优雅,特别是因为仿函数类型恰好是第二个模板参数,但它是一种解决方法。

于 2012-07-13T17:23:26.570 回答
1

实现析构函数时,可能需要实现复制构造函数和复制赋值运算符。这被称为三法则

一旦你正确地实现了这两个方法,你就会发现它并没有调用析构函数两次,而是创建一个副本并销毁每个副本。

于 2010-11-17T00:05:11.287 回答
0

for_each按值获取函子并返回它的副本作为返回值,因此修改的不是您的,而是随后返回的函数concatenator的本地。for_each您应该将代码更改为以下内容:

 Concatenator concatenator;
 concatenator = for_each(list.begin(), list.end(), concatenator);

现在concatenator,您将拥有修改后的函子。

关于析构函数调用:它们在for_each返回时开始;第一个是 的参数之一for_each,第二个是它返回的副本for_each(被丢弃),第三个是原始concatenator对象的一个​​,在程序结束时被破坏。

于 2010-11-17T00:10:52.227 回答