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;
}

連結子クラスは、通常のファンクターとして実装されます。

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
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 、値for_each によって返されるため、 get の 3 つのインスタンスがConcatenator作成されます。

  1. を使用して 1 つのインスタンスを作成します。Concatenator concatenator;
  2. このオブジェクトをに渡すと、値for_eachで受け取るためコピーさfor_eachれます
  3. for_eachファンクターを値で返し、別のコピーを作成します

これら 3 つのオブジェクトはそれぞれ破棄されるため、デストラクタは 3 回呼び出されます。

于 2010-11-17T00:06:25.867 に答える
1

デストラクタを実装する場合、コピー コンストラクタとコピー代入演算子も実装する必要がある可能性があります。これは3 つのルールとして知られています。

これら 2 つのメソッドを正しく実装すると、デストラクタが 2 回呼び出されるのではなく、コピーが作成され、それぞれのコピーが破棄されることがわかります。

于 2010-11-17T00:05:11.287 に答える
1

他の回答では、あなたの場合の問題は functor オブジェクトがvalue によってfor_each渡され、そこから返されることであるとすでに説明されています。for_each

ただし、 の宣言がこの動作の原因であることは事実ですがfor_each、最後の言葉は C++ のテンプレート引数推論メカニズムによって語られます。言語の規則に従って、この通話では

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

for_eachtemplateの 2 番目の引数はas ではConcatenatorなく as と推定されるためConcatenator &、値渡しセマンティクスになります。

テンプレート引数を明示的に指定し、2 番目のテンプレート引数の参照型を主張することで、推論をオーバーライドできます。

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

これにより、コピーが完全になくなり、値渡しのセマンティクスが参照渡しのセマンティクスに置き換えられます ( の戻り値もカバーしますfor_each)。特に functor 型がたまたま2 番目のテンプレート引数であるため、これはエレガントに見えませんが、回避策です。

于 2012-07-13T17:23:26.570 に答える
0

for_eachファンクターを値で受け取り、そのコピーを戻り値として返すため、concatenator変更されるのはあなたではなく、関数に対してローカルfor_eachなものが返されます。コードを次のように変更する必要があります。

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

これconcatenatorで、変更されたファンクターができました。

デストラクタ呼び出しについて: それらはfor_each戻ったときに始まります。最初のものは のパラメータの 1 つであり、2 番目のものは によって返された(破棄される)for_eachそのコピーの 1 つであり、3 番目のものは元のオブジェクトの 1 つであり、プログラムの終了時に破棄されます。for_eachconcatenator

于 2010-11-17T00:10:52.227 に答える