12

C++11 では、場合によってはオブジェクトを値で渡し、他の場合は const-reference で渡すように案内されます。ただし、このガイドラインは、メソッドのインターフェースやクライアントによる使用目的だけでなく、メソッドの実装にも依存します。

インターフェイスを書くとき、それがどのように実装されるかわかりません。メソッド署名を書くための良い経験則はありますか? たとえば、次のコード フラグメントでは、 or を使用する必要がありますBar1Bar2?

class IFoo
{
public:
    virtual void Bar1(std::string s) = 0;
    virtual void Bar2(const std::string& s) = 0;
};

正しい署名が実装に依存することに同意する場合は、ここで読むのをやめてください。ここに、私がそう信じる理由を示す例があります。

次の例では、文字列を値で渡す必要があります。

class Foo
{
    std::string bar;

    Foo(std::string byValue)
        : bar(std::move(byValue))
    {
    }
};

これで、すべてのケースで効率的な方法で Foo をインスタンス化できます。

Foo foo1("Hello world"); // create once, move once
Foo foo2(s); // the programmer wants to copy s. One copy and one move
Foo foo3(std::move(t)); // the programmer does not need t anymore. No copy at all

それ以外の場合は、const 参照によってオブジェクトを渡すことを好みます。たとえば、次の場合、引数をコピー/保存したくありません。そのメソッドを使用するだけです。

void DoStuff(const std::string& byRef)
{
    std::cout << byRef.length() << std::endl;
}

上記の方法のすべての可能な使用法は、すでに可能な限り効率的です。

アップデート

const-reference の代替の問題を示すのを忘れていたと思います。上記のクラスFooがこのように実装されている場合:

class Foo
{
    std::string bar;

    Foo(const std::string& byRef)
        : bar(byRef)
    {
    }
};

次に、次の結果が得られます。

Foo foo1("Hello world"); // Here we would have one more copy of the string. It is less efficient.
Foo foo2(s);             // One copy, like before
Foo foo3(std::move(t));  // Irrelevant here.

アレックス。

4

3 に答える 3

4

ここには「すべての理論」はありません。あなたはそれを正しく理解しました、問題があります。少し前に自分で直面したことを覚えています。

私の結論はここから始まりました:

アプリケーションとフレームワーク/ライブラリの開発

クライアントが開発者の場合、この仕事ははるかに困難です。難しいだけでなく、明確なガイドラインもありません。偉大なフレームワークの設計者は、たまたまリスクを冒して報われたために名声を得ました。同時に、別の宇宙では、彼らのリスクは報われなかった可能性があります. これは、フレームワークの評価は、その使用の増加の方向性と、アプリケーション ドメインよりも推論がはるかに難しい主観的な意見に依存するためです。

したがって、この場合、明確な答えはありません。幸いなことに、ここでは主にアプリケーション開発に関心があると思います。それでは、それに取り掛かりましょう。

出発点: 私たちはアプリケーションを開発しています

これは大きな違いになります。システムがどこに向かっているのか、そしてどのようなコードが有用であることが判明するのかについて、より良いアイデアを持っているはずだからです。私たちは予言者ではありませんが、同時に、この仮定により、要件に関する知識とお客様のニーズに基づいた直感を信頼することができます (少なくとも理解できた限りでは)。 )。

この時点で、これを 2 つのケースに分けることができます。

実装への抽象化

実装の前に抽象化を定義することが有益である、または必要でさえある場合があります。このような場合、抽象化を適切に定義する前に、問題についてさらに多くの調査が必要であることを認識する必要があります。たとえば、ドメインは同期ですか、それとも非同期ですか? シリアルかパラレルか?レベルが高いか低いか?そして、他のはるかに具体的な質問。

一部の極端なアジャラーは、コードを書いて後で修正できると信じ込ませます。しかし、現実に直面すると、その主張は非常に簡単に反証されます。希望を見つけた場合は、自分でテストして、重要な発見があったかどうかを報告することをお勧めします. 私の個人的な経験と、私がこの問題に取り組もうとしていると思ったことは、大規模なプロジェクトでは、このアプローチが非常に問題があることを示唆しています。

この場合の結論は、実際に先に抽象化を定義する必要がある場合は、実装について非常に優れたアイデアをすでに持っているはずです。それについてより良いアイデアを持っているほど、実際に適切な抽象化に成功する可能性が高くなります。

抽象化への実装

これが私のデフォルトの選択です。いろいろ言われてきました。「フレームワークは抽出する必要があります」、「ドロップするまで抽出する」、さらには「構成よりも規約」でさえ、概念にいくつかの類似点があります。

基本的にこれは、必要なコンポーネントを必要に応じて実装することを意味しますが、何が起こっているのかを注意深く監視してください。ここでの秘訣は、開発と保守の点で実際に役立つ方法で抽象化する機会を探すことです。

これは、多くの場合、必要なことを行うクラスとして出てきますが、それ以上です。その場合、交差点をより一般的なケースに抽象化します。開発中、必要に応じてこのプロセスを繰り返します。

引っかからず、地に足をつけたままにすることが重要です。私は多くの抽象化の試みがうまくいかず、それを使用する何千行ものコードを読む以外に、その名前について推論し、その意図を推測する方法がないところまで見てきました。たとえば、私が取り組んでいる現在のコード ベースでは、呼び出されるべき型が呼び出されていImageますBinaryData。コード全体で、それを具体的な (画像) として扱うと同時に、抽象的な概念として扱う試みが行われています。

総括する

私がいつも思い出すように、ベスト プラクティスは、既知のベスト プラクティスを問題に合わせて調整することであり、その逆ではありません。それができない場合は、その問題が興味深いため、さらに注意を払う必要があり、少し独創的な考えが必要なのかもしれません。

于 2013-09-03T15:06:21.903 に答える
0

それは間違いなく実装に依存するべきだと思います。あなたの質問から示唆されているように、完全に「常に優れた」署名を除いて、唯一の賢明なことは、現在の実装を最適化する方法で署名を選択することです。コードの前にインターフェースを書く場合 - 経験に基づいた推測を行い、署名にコミットする前に最初の実装を待つことができるように自分自身を操作してみてください。

ここで有効な言葉は「最初」と「現在」です。間違えるとどうなる?後の段階で署名がコードの最適化を妨げている場合はどうなるでしょうか? できることは次のとおりです。

義務はありません

すぐに変更できる場合は、変更してください。「コミットメントなし」の定義から導かれますよね?

API にコミット

具体的な例として、あなたが間違った選択をして、次のようにしたとします。

virtual void DoStuff(std::string s) = 0;

DoStuffただし、結局のところ、コピーを実行する必要はありません (元の実装と同じ)。できることは次のとおりです。

// stuff.h
virtual void DoStuff_Optimized(const std::string & s);
virtual void DoStuff(std::string s);

// stuff.cc
virtual void DoStuff_Optimized(const std::string & s);
{
    // Fast implementation of DoStuff, no copying necessary
    std::cout << s.length() << std::endl;
}

virtual void DoStuff(std::string s)
{
    DoStuff_Optimized(s);
}

既存のクライアントのパフォーマンスは低下します。新しいクライアントはそのOptimizedバージョンを使用できます。

ABIへのコミットメント

残念ながら、この時点でできることは何もないかもしれません。ただし、注意すれば、「Committed to API」アクションを実行できる場合があります。(特に、私の例では ABI 互換性が維持されません)。

于 2013-09-08T19:59:39.037 に答える