shorten - c++ using inheritance




なぜ "using namespace std"は悪い習慣とみなされますか? (20)

グローバルに使用しないでください

グローバル使用されている場合のみ「悪い」とみなさます。 理由:

  • あなたがプログラミングしている名前空間を混乱させます。
  • 読者は、多くのusing namespace xyzを使用すると、特定の識別子がどこから来るのかを知ることができません。
  • あなたのソースコードの他の読者にとって本当であることは、それの最も頻繁な読者にとってはより真実です。 一年か二年後に戻って見てみましょう...
  • using namespace stdusing namespace stdすることについて話しているだけでは、あなたが手に入れたものすべてを認識していない可能性があります。別の#includeを追加するか、新しいC ++リビジョンに移動すると、あなたは気づいていない名前の競合が発生する可能性があります。

ローカルで使用することができます

それをローカルで(ほぼ)自由に使用してください。 これはもちろん、 std::反復を防ぎます。 - 繰り返しも悪いです。

ローカルで使用するためのイディオム

C ++ 03には、クラスのswap機能を実装するためのイディオム(ボイラープレートコード)がありました。 あなたが実際にusing namespace stdてローカルに使うことを提案しました - または少なくともusing std::swap

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

これは以下の魔法を行います:

  • コンパイラはvalue_ std::swapを選択します。つまり、 void std::swap(int, int)です。
  • オーバーロードのvoid swap(Child&, Child&)実装されている場合、コンパイラはそれを選択します。
  • そのオーバーロードがない場合、コンパイラはvoid std::swap(Child&,Child&) 、これらを最適に交換しようとします。

C ++ 11では、このパターンをこれ以上使用する必要はありません。 std::swapの実装を変更して、潜在的なオーバーロードを見つけ出し、それを選択しました。

私はコード内でusing namespace stdて書くusing namespace stdが間違っていること、そしてstd::coutstd::cin直接使うべきだということusing namespace std他の人から聞きました。

なぜusing namespace stdが悪い習慣と考えられるのですか? それは非効率的であるか、あいまいな変数( std名前空間内の関数と同じ名前を持つ変数)を宣言する危険がありますか? パフォーマンスに影響を与えますか?


  1. あなたは、あなたとは異なるスタイルとベストプラクティスの意見を持っている人が書いたコードを読むことができる必要があります。

  2. coutだけを使用している場合、誰も混乱しません。 しかし、たくさんの名前空間が飛んできて、このクラスを見ると、そのクラスが何をしているのか正確には分からないので、名前空間を明示的に並べ替えてコメントにします。 一見すると「ああ、これはファイルシステムの操作です」または「ネットワークのことをすること」です。


これはパフォーマンスにはまったく関係ありません。 しかしこれを考えてみましょう:FooとBarという2つのライブラリを使用しています:

using namespace foo;
using namespace bar;

すべてうまく動作し、あなたは問題なくBarからFooとQuux()からBlah()を呼び出すことができます。 しかし、 Quux()という関数を提供するFoo 2.0の新しいバージョンにアップグレードします。 Foo 2.0とBarの両方がグローバル名前空間にQuux()をインポートします。 これは、特に関数のパラメータが一致する場合には、修正するために多少の努力を払うつもりです。

foo::Blah()bar::Quux()使用した場合、 foo::Quux()導入は非イベントでした。


それはすべて複雑さを管理することです。 ネームスペースを使用すると、あなたがしたくないことが起こり、デバッグするのが難しくなる可能性があります。 場所の上でstd :: allを使用することは、読むのが難しい(より多くのテキストとそのすべて)。

コースの馬 - あなたの複雑さを管理し、どのようにしてできるかを最大限に実感してください。


コードを見て、それが何をしているか知っているのはいいことです。 私がstd::coutを参照すると、 stdライブラリのcoutストリームがわかります。 もし私がcoutを見たら、私は知らない。 それ stdライブラリのcoutストリームである可能性があります。 または、 int cout = 0;となる可能性がありint cout = 0; 同じ機能では10行高い または、そのファイル内のcoutという名前のstatic変数。 それは何でもかまいません。

今は100万行のコードベースをとりますが、これは特に大きなものではありません。あなたはバグを探しています。これは、この100万行に1行があることを知っていることを意味します。 cout << 1; coutというstatic int読み込み、1ビット左にシフトして結果をcoutすることができます。 バグを探して、私はそれをチェックしなければならないでしょう。 私は本当にstd::coutを見るのが本当に好きですか?

あなたが先生であり、生計のためのコードを書いたり維持したりする必要がないというのは、本当に良いアイデアのようです。 私はコードを見るのが大好きです。(1)私はそれが何をしているか知っています。 (2)私はそれを書いている人が自分が何をしているのか分かっていたと確信しています。


別の理由は驚きです。

もし私がcout << blahを見たら、 std::cout << blah代わりに

私はこのcoutは何だと思いますか? それは正常なcoutですか? それは特別なものですか?


名前空間stdを使用する例は、アルゴリズムライブラリの関数でもあるcountのあいまいさのために複雑化エラーをスローします。

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout<<count<<endl;
}

名前空間は名前付きスコープです。 名前空間は、関連する宣言をグループ化し、別々の項目を分離しておくために使用されます。 たとえば、別々に開発された2つのライブラリでは、同じ名前を使用して異なる項目を参照することがありますが、ユーザーは引き続き両方を使用できます。

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    / / ...
}
namespace Yourlib{
    class Stack{ /* ... */ };
    / / ...
}
void f(int max) {
    Mylib: :Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

名前空間の名前を繰り返すことは、読者と作家の双方にとって気を散らすことになります。したがって、特定の名前空間からの名前が明示的な修飾なしで利用可能であることを述べることが可能です。例えば:

void f(int max) {
    using namespace Mylib; / / make names from Mylib accessible
    Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

ネームスペースは、異なるライブラリや異なるバージョンのコードを管理するための強力なツールを提供します。特に、非ローカル名への参照をどのように明示的に指定するかというプログラマーの選択肢を提供します。

出典:Bjarne StroustrupによるC ++プログラミング言語の概要


検討する

// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // uh oh
};

これは簡単な例です.20個のインクルードファイルとその他のインポートファイルがあれば、問題を把握するために多くの依存関係があります。 それについてのより悪いことは、矛盾する定義に応じて、他のモジュールで無関係なエラーを取得できることです。

恐ろしいことではありませんが、ヘッダーファイルやグローバル名前空間で頭痛を使うことはありません。 非常に限定された範囲でそれを行うことはおそらく大丈夫ですが、私の機能がどこから来ているかを明確にするために余分な5文字をタイプすることに問題はありませんでした。


短いバージョン:ヘッダーファイルに宣言やディレクティブを使用してグローバルを使用しないでください。 実装ファイルで自由に使用してください。 Herb SutterとAndrei Alexandrescuがこの問題についてC ++のコーディング標準で強調しておかなければならないことは何ですか?

概要

ネームスペースの使用はあなたの便宜のためであり、他人に与えることではありません。#includeディレクティブの前に、using宣言またはusingディレクティブを記述しないでください。

必然性:ヘッダファイルでは、ディレクティブや宣言を使用して名前空間レベルを記述しないでください。 代わりに明示的にすべての名前空間を修飾します。 (2番目のルールは最初のルールの後に続きます。なぜなら、ヘッダーは他のヘッダーの#includeが後に出現する可能性があることを決して知ることができないからです。)

討論

つまり、#includeディレクティブの後に実装ファイルで宣言とディレクティブを自由に使用してネームスペースを使用することができます。 繰り返しの宣言にもかかわらず、宣言とディレクティブを使った名前空間は邪悪ではなく、名前空間の目的を破るものではありません。 むしろ、それらは名前空間を使用可能にするものです。


私はここの他の人に同意しますが、読みやすさに関する懸念に対処したいと思います。ファイル、関数、クラス宣言の先頭にtypedefを使用するだけで、そのすべてを避けることができます。

私は通常クラス内のメソッドが同様のデータ型(メンバ)を扱う傾向があり、typedefはクラスのコンテキストで意味のある名前を割り当てる機会であるため、クラス宣言で使用します。これは実際にクラスメソッドの定義における読みやすさを助けます。

//header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

実装では:

//cpp
Lines File::ReadLines()
{
    Lines lines;
    //get them...
    return lines;
}

とは対照的に:

//cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    //get them...
    return lines;
}

または:

//cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    //get them...
    return lines;
}

私はそれをグローバルに使うべきではないことに同意しnamespaceが、 namespaceようにローカルで使うのはそれほど悪くありません。 以下は"The C ++ Programming Language"の例です:

namespace My_lib {

    using namespace His_lib; // everything from His_lib
    using namespace Her_lib; // everything from Her_lib

    using His_lib::String; // resolve potential clash in favor of His_lib
    using Her_lib::Vector; // resolve potential clash in favor of Her_lib

}

この例では、潜在的な名前の衝突とその構成から生じるあいまいさを解決しました。

その中で明示的に宣言された名前( His_lib::StringようなHis_lib::String宣言によって宣言された名前を含む)は、usingディレクティブ( using namespace Her_lib )によって別のスコープでアクセス可能にされた名前よりも優先されます。


私は最近、 Visual Studio 2010に関する苦情を受けました。 ほとんどのソースファイルには、次の2行が含まれていました。

using namespace std;
using namespace boost;

多くのBoost機能はC ++ 0x標準に移行しており、Visual Studio 2010にはC ++ 0x機能が多く、突然これらのプログラムはコンパイルされませんでした。

したがって、 using namespace X;避けusing namespace X; 将来の校正の一形態であり、使用中のライブラリやヘッダーファイルへの変更がプログラムを破壊しないようにする方法です。


経験豊富なプログラマは、問題を解決するものを使用し、何が新しい問題を引き起こすのかを避け、ヘッダーファイルレベルの指示を使用しないでください。

経験豊富なプログラマーは、ソースファイル内の名前の完全な修飾も避けようとします。 これに対してマイナーな理由は、 正当な理由がない限り、コードが少なくても十分なコードを書くのはエレガントではないということです。 主な理由は、引数依存ルックアップ(ADL)を無効にすることです。

これらの良い理由は何ですか? 時には、プログラマが明示的にADLを無効にしたいときや、曖昧さをなくしたいときもあります。

だから以下はOKです:

  1. 関数のインプリメンテーション内でディレクティブと宣言を使用する関数レベル
  2. ソースファイル内の宣言を使用したソースファイルレベル
  3. (時には)source-file-level using-directives

あなたの質問に答えるために、私は実際にこのように見ています:多くのプログラマー(すべてではない)が名前空間stdを呼び出します。したがって、ネームスペースstdにある名前と同じ名前を使用したり使用したりすることを習慣にしないでください。それは大したことではありますが、厳密に言えば可能な一貫した言葉や仮名の数に比べるとあまり違いはありません。

私は本当に...「存在していることに頼らないでください」と言っているのは、それが存在していないことに頼るようにあなたを設定することだけです。あなたはコードスニペットを借りて、常にそれらを修復する問題を常に持っています。限られた範囲でユーザ定義のものや借用したものをそのままにしておき、グローバルではあまり使わないようにしてください(正直なところグローバルは「コンパイル」、「正気」の目的のために最後の手段にすべきです)。本当に私はstdを使うと "cout"と "std :: cout"の両方で動作するので、あなたの先生からの悪いアドバイスだと思うが、stdを使わないのは "std :: cout"のためだけだ。あなた自身のコードを書くのに十分なことはありません。

注意:コンパイラがどのように動作するかについて実際に学ぶまで、効率の問題にあまり集中しないでください。経験を積んだ小さなコーディングでは、良いコードを何かに単純化してどれくらい単純化できるかを理解する前に、多くのことを学ぶ必要はありません。あなたがC.ですべてを書いたように、すべてのビットは単純です。グッドコードは必要なだけ複雑です。


これは悪い習慣であり、しばしばグローバルな名前空間汚染として知られています。複数の名前空間に同じ名前の関数がシグニチャ付きである場合に問題が発生する可能性があります。コンパイラがどの関数を呼び出すかを決定するのはあいまいなので、関数呼び出しのように名前空間を指定するときは避けることができますstd::cout。お役に立てれば。 :)


ソフトウェアやプロジェクトのパフォーマンスが悪化することはありません。ソースコードの先頭にネームスペースを含めることは悪くありません。using namespace std指示の包含はあなたの必要性およびあなたがソフトウェアまたはプロジェクトを開発する方法によって変わります。

namespace stdC ++標準関数や変数が含まれています。この名前空間は、C ++標準関数を頻繁に使用する場合に便利です。

このpage記載されているとおり:

名前空間stdを使用するステートメントは、通常、悪い習慣とみなされます。このステートメントの代わりに、型を宣言するたびにscope演算子(::)を使用して、識別子が属する名前空間を指定することができます。

そしてこの意見を参照してください:

ネームスペースを大量に使用し、何も衝突しないことがわかっているときは、ソースファイルで "using namespace std"を使用しても問題ありません。

using namespace stdその名前空間からすべての関数と変数を呼び出すので、あなたのソースファイルにこれを含めることは悪い習慣であると言う人もいました。あなたが含まれている別の関数と同じ名前の新しい関数を定義したいときはnamespace std、関数をオーバーロードし、コンパイルや実行のために問題が生じる可能性があります。コンパイルや実行は期待どおりに行われません。

このpage記載されているとおり:

このステートメントはstd ::をタイプするのを止めますが、stdネームスペースで定義されたクラスまたはタイプにアクセスするときはいつでも、stdネームスペース全体をプログラムの現在のネームスペースにインポートします。なぜこれがあまり良いことではないのかを理解するためにいくつかの例を挙げてみましょう

...

開発の後の段階で、 "foo"と呼ばれるライブラリ(たとえば)でカスタム化された別のバージョンのcoutを使用したいと考えています。

...

どのようにしてライブラリが指し示すのか、あいまいさがあることに注目してください。コンパイラはこれを検出し、プログラムをコンパイルしません。最悪の場合、プログラムがまだコンパイルされている可能性がありますが、誤った関数を呼び出すことがあります。


懸念を明確にする具体的な例。2つのライブラリfooとbarがそれぞれ独自の名前空間を持っている状況を想像してみてください。

namespace foo {
    void a(float) { /* does something */ }
}

namespace bar {
    ...
}

次に、あなた自身のプログラムでfooとbarを以下のように使用するとしましょう:

using namespace foo;
using namespace bar;

void main() {
    a(42);
}

この時点で、すべては問題ありません。あなたがあなたのプログラムを実行するとき、それは何かをします。しかし、後でバーを更新し、それが次のように変更されたとしましょう:

namespace bar {
    void a(float) { /* does something completely different */ }
}

この時点でコンパイラエラーが発生します:

using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}

だから、あなたが意味するものを明確にするために、あるメンテナンスをする必要がありますfoo::a。これはおそらく望ましくないかもしれませんが、幸いにもそれは非常に簡単です(コンパイラマークがあいまいであることfoo::をすべての呼び出しの前に追加するだけaです)。

しかし代わりに代わりにバーが代わりにこのように見える代わりのシナリオを想像してみてください:

namespace bar {
    void a(int) { /* does something completely different */ }
}

この時点で、あなたの呼び出しはa(42)突然、「何か」のbar::a代わりにfoo::aとバインドされ、「何か全く違う」ことが起こります。コンパイラの警告などはありません。あなたのプログラムは、以前とはまったく異なる何かを静かに始めるだけです。

ネームスペースを使用すると、このようなシナリオが発生する可能性があります。そのため、ネームスペースを使用している人に不快感があります。名前空間が多くなればなるほど紛争のリスクが高くなるので、他の名前空間よりも(名前空間内の物の数に起因して)名前空間stdを使用する方が不快に感じるかもしれません。

最終的にこれは書き込み可能性と信頼性/保守性のトレードオフです。可読性もまた影響するかもしれませんが、私はそれがどちらの方向に進むのか議論を見ることができます。通常、信頼性と保守性は重要だと言いますが、この場合、貴重な信頼性/保守性の影響のために書き込み可能なコストを常に支払うことになります。「最良の」トレードオフは、プロジェクトと優先順位を決定します。


私の経験から言えば、複数のライブラリを使用している場合はcout、しかし、別の目的のために間違って使用することがありますcout

例えば、私が入力した場合、using namespace std;およびusing namespace otherlib;、ちょうどcoutのタイプ(両方であることを起こるもの)ではなく、std::cout(または'otherlib::cout')、あなたは間違ったものを使用して、エラーが発生する可能性があります、それが使用することをはるかに効果的かつ効率的ですstd::cout


私はそれが必ずしもすべての条件下で悪い習慣であるとは思わないが、あなたがそれを使うときは注意する必要がある。ライブラリを作成する場合は、おそらく、名前空間でスコープ解決演算子を使用して、ライブラリが他のライブラリとぶつかるのを防ぐ必要があります。アプリケーションレベルのコードについては、私はそれに間違って何も表示されません。





c++-faq