5

バックグラウンド

内部で vector<std::string> を使用するコンテナ クラスがあります。内部ベクトルに対してpush_back()を実行するメソッドAddChar(std::string)をこのラッパー クラスに提供しました。私のコードでは、コンテナに複数のアイテムを追加する必要があります。そのために私は使用しなければなりません

container.AddChar("First");
container.AddChar("Second");

これにより、コードが大きくなります。そのため、より簡単にするために、演算子 << をオーバーロードする予定です。書けるように

container << "First" << "Second"

2 つのアイテムが基になるベクターに追加されます。

これが私がそのために使用したコードです

class ExtendedVector
{
private:
    vector<string> container;

public:
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
        cont.AddChar(str);
        return cont;
    }

    void AddChar(const std::string str)
    {
        container.push_back(str);
    }

    string ToString()
    {
        string output;
        vector<string>::iterator it = container.begin();
        while(it != container.end())
        {
            output += *it;
            ++it;
        }
        return output;
    }
};

期待どおりに動作しています。

質問

  1. 演算子のオーバーロードは正しく記述されていますか?
  2. このような状況で演算子をオーバーロードすることは良い習慣ですか?
  3. このコードにパフォーマンスの問題やその他の問題はありますか?

何かご意見は?

編集

優れたコメントを聞いた後、<< をオーバーロードしないことにしました。ここでは意味がありません。演算子のオーバーロード コードを削除しました。これが最終的なコードです。

class ExtendedVector
{
private:
    vector<string> container;

public:

    ExtendedVector& AddChar(const std::string str)
    {
        container.push_back(str);
        return *this;
    }

         .. other methods
}

これにより、追加できます

container.AddChar("First").AddChar("Second")

C# では、params キーワードを使用してこれをより簡単に行うことができます。コードは次のようになります

void AddChar(params string[] str)
{
    foreach(string s in str)
       // add to the underlying collection
}

C++ では、...を使用してパラメーターの可変長を指定できることを知っています。しかし、知る限り、タイプセーフではありません。そうすることは推奨される方法ですか?書けるように

container.AddChar("First","Second")

返信ありがとうございます。

4

7 に答える 7

8

演算子のオーバーロードは正しく記述されていますか?

それはそうですが、もっとうまくやることができます。他の誰かが言及したように、関数は既存のパブリック関数から完全に定義できます。それらだけを使用するようにしないのはなぜですか?今のところ、それは実装の詳細に属していることを意味する友人です。operator<< をメンバーとしてクラスに入れる場合も同様です。ただし、 operator<< を非メンバー非フレンド関数にします。

class ExtendedVector {
    ...
};

// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
    cont.AddChar(str);
    return cont;
}

クラスを変更すると、 operator<< がまだ機能するかどうかわからなくなります。しかし、あなたの operator<< 完全にパブリック関数のみに依存している場合は、クラスの実装の詳細のみに変更が加えられた後にのみ機能することを確認できます。わーい!

このような状況で演算子をオーバーロードすることは良い習慣ですか?

別の男が再び言ったように、これは議論の余地があります。多くの場合、演算子のオーバーロードは一見「きれい」に見えますが、来年は地獄のように見えます。演算子<<の場合、これはOKな使い方だと思います。ストリームの挿入演算子としての使用はよく知られています。そして、次のような場合にそれを広く使用するQtおよびKDEアプリケーションを知っています

QStringList items; 
items << "item1" << "item2";

同様のケースは、文字列でプレースホルダーの引数を渡すためにboost.format再利用されます。operator%

format("hello %1%, i'm %2% y'old") % "benny" % 21

もちろん、そこで使用することも議論の余地があります。しかし、printf 形式の指定での使用はよく知られているため、そこでの使用も問題ありません。しかし、いつものように、スタイルも主観的なものなので、塩の粒でそれを取ってください :)

タイプセーフな方法で可変長引数を受け入れるにはどうすればよいですか?

同種の引数を探している場合は、ベクトルを受け入れる方法があります。

void AddChars(std::vector<std::string> const& v) {
    std::vector<std::string>::const_iterator cit =
        v.begin();
    for(;cit != v.begin(); ++cit) {
        AddChar(*cit);
    }
}

しかし、それを渡すのは本当に快適ではありません。ベクトルを手動で構築してから渡す必要があります... vararg スタイルの関数については、すでに正しい感覚を持っているようです。この種のコードには使用しないでください。また、C コードとのインターフェイスまたはデバッグ機能を使用する場合にのみ使用してください。このケースを処理する別の方法は、プリプロセッサ プログラミングを適用することです。これは高度なトピックであり、非常にハックです。アイデアは、おおよそ次のように、上限までオーバーロードを自動的に生成することです。

#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
    /* now access arg0 ... arg(X-1) */ \
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \
    GEN_PRINT_ARG1(X, AddChar, arg) \
}

/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)

それが疑似コードです。ブースト プリプロセッサ ライブラリはこちらでご覧いただけます

次の C++ バージョンでは、はるかに優れた可能性が提供されます。初期化子リストを使用できます:

void AddChars(initializer_list<std::string> ilist) {
    // range based for loop
    for(std::string const& s : ilist) {
        AddChar(s);
    }
}

...
AddChars({"hello", "you", "this is fun"});

次の C++ では、可変個引数のテンプレートを使用して任意の多くの (混合型) 引数をサポートすることも可能です。GCC4.4 はそれらをサポートします。GCC 4.3 はすでに部分的にサポートしています。

于 2009-03-02T04:35:14.243 に答える
3

このような状況で演算子をオーバーロードすることは良い習慣ですか?

私はそうは思わない。オペレーターが過負荷になっていることを知らない人にとっては、非常に混乱します。説明的なメソッド名に固執し、入力している余分な文字を忘れてください。それだけの価値はありません。あなたのメンテナー (または 6 か月後のあなた自身) はあなたに感謝します。

于 2009-03-02T03:55:40.233 に答える
3

1) はい、ただし AddChar は public であるため、 である必要はありませんfriend

2) これには議論の余地があります。<<「奇妙な」ものに対する過負荷が少なくともしぶしぶ受け入れられているオペレーターであるような立場にあります。

3) 明らかなことは何もありません。いつものように、プロファイリングはあなたの友達です。不必要なコピーを避けるために、文字列パラメーターを const 参照 ( ) に渡すことを検討することをお勧めしAddCharますoperator<<const std::string&

于 2009-03-02T03:56:04.587 に答える
2

ベクトルには通常、オーバーロードされた左シフト演算子がないため、個人的にはそのようにオーバーロードしないことをお勧めします-それは実際にはイディオムではありません;-)

代わりに、次のように AddChar から参照を返します。

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

だからあなたはそれからすることができます

container.AddChar("First").AddChar("Second");

これは、ビットシフト演算子よりもそれほど大きくありません。

(値ではなく参照によって文字列を渡すことに関する Logan のコメントも参照してください)。

于 2009-03-02T04:07:08.787 に答える
2

この場合の演算子のオーバーロードは、コードが読みにくくなるため、お勧めできません。正当な理由により、標準std::vectorには要素をプッシュするための要素もありません。

呼び出し元のコードが長すぎることが心配な場合は、オーバーロードされた演算子の代わりにこれを検討できます。

container.AddChar("First").AddChar("Second");

AddChar()returnがあれば可能です*this

こんなtoString()機能があるなんておかしい。その場合、代わりoperator<<に to output をストリームに使用するのが標準です! したがって、演算子を使用する場合は、toString()関数をoperator<<.

于 2009-03-02T04:13:22.830 に答える
1

ここでは、演算子が正しくオーバーロードされていません。オペレーターはクラスのメンバーになることができるため、オペレーターをフレンドにする理由はありません。Friend は、クラスの実際のメンバーではない関数用です (オブジェクトを cout または ofstreams に出力できるように ostream に対して << をオーバーロードする場合など)。

実際にオペレーターになりたいもの:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

通常、通常よりも何かを実行させる方法で演算子をオーバーロードすることは、悪い習慣と見なされます。<< は通常ビット シフトなので、このようにオーバーロードすると混乱する可能性があります。明らかに、STL は「ストリーム挿入」のために << をオーバーロードするため、同様の方法で使用するためにそれをオーバーロードすることは理にかなっているかもしれません。しかし、それはあなたがしていることのようには思えないので、おそらく避けたいと思うでしょう.

演算子のオーバーロードは通常の関数呼び出しと同じであるため、パフォーマンスの問題はありません。呼び出しはコンパイラによって自動的に行われるため、隠されているだけです。

于 2009-03-02T04:08:11.547 に答える
0

これは物事をかなり混乱させるでしょう。私は std::cin と同じ構文を変数に使用します:

std::cin >> someint;

"First" >> container;

このように、少なくとも挿入演算子です。何かに << オーバーロードされた演算子がある場合、私はそれが何かを出力していることを期待しています。std::cout と同じです。

于 2009-03-02T04:23:23.100 に答える