6

私のプロジェクトの1つで、C++フィールドに向かいます。基本的に私はJavaのバックグラウンドを持っており、Javaパッケージの概念がC++の世界でどのように実現されているのか疑問に思っていました。これにより、名前空間のC++の概念にたどり着きました。

これまでのところ、名前空間はまったく問題ありませんが、ヘッダーファイルに関しては、完全修飾クラス名、using-directives、using-declarationsに関しては非効率的になっています。

この問題の非常に良い説明は、ハーブサッターによるこの記事です。

私が理解しているように、これはすべて要約すると次のようになります。ヘッダーファイルを作成する場合は、常に完全修飾型名を使用して、他の名前空間の型を参照してください。

これはほとんど受け入れられません。C ++ヘッダーは通常、クラスの宣言を提供するため、読みやすさの最大値が最優先されます。異なる名前空間からの各タイプを完全に修飾すると、多くの視覚的なノイズが発生し、最終的にヘッダーの可読性がある程度低下し、名前空間を使用するかどうかという疑問が生じます。

それにもかかわらず、私はC ++名前空間を利用したいので、質問にいくつかの考えを入れます:C ++ヘッダーファイルの名前空間の悪を克服する方法は?いくつかの調査の結果、typedefはこの問題の有効な解決策になると思います。

次に、パブリッククラススコープのtypedefを使用して他の名前空間から型をインポートする方法を示すC++サンプルプログラムを示します。プログラムは構文的に正しく、MinGWW64で正常にコンパイルされます。これまでのところ良いですが、このアプローチがヘッダーからusingキーワードをうまく削除するかどうかはわかりませんが、私が単に気付いていない別の問題をもたらします。ハーブサッターが説明したようなトリッキーなもの。

つまり、C ++を完全に理解しているすべての人に、以下のコードを確認して、これが機能するかどうかを知らせてください。あなたの考えをありがとう。

MyFirstClass.hpp

#ifndef MYFIRSTCLASS_HPP_
#define MYFIRSTCLASS_HPP_

namespace com {
namespace company {
namespace package1 {

class MyFirstClass
{
public:
    MyFirstClass();
    ~MyFirstClass();

private:

};

} // namespace package1
} // namespace company
} // namespace com

#endif /* MYFIRSTCLASS_HPP_ */

MyFirstClass.cpp

#include "MyFirstClass.hpp"

using com::company::package1::MyFirstClass;

MyFirstClass::MyFirstClass()
{
}

MyFirstClass::~MyFirstClass()
{
}

MySecondClass.hpp

#ifndef MYSECONDCLASS_HPP_
#define MYSECONDCLASS_HPP_

#include <string>
#include "MyFirstClass.hpp"

namespace com {
namespace company {
namespace package2 {

    /*
     * Do not write using-declarations in header files according to
     * Herb Sutter's Namespace Rule #2.
     *
     * using std::string; // bad
     * using com::company::package1::MyFirstClass; // bad
     */

class MySecondClass{

public:
    /*
     * Public class-scoped typedefs instead of using-declarations in
     * namespace package2. Consequently we can avoid fully qualified
     * type names in the remainder of the class declaration. This
     * yields maximum readability and shows cleanly the types imported
     * from other namespaces.
     */
    typedef std::string String;
    typedef com::company::package1::MyFirstClass MyFirstClass;

    MySecondClass();
    ~MySecondClass();

    String getText() const; // no std::string required
    void setText(String as_text); // no std::string required

    void setMyFirstInstance(MyFirstClass anv_instance); // no com::company:: ...
    MyFirstClass getMyFirstInstance() const; // no com::company:: ...

private:
    String is_text; // no std::string required
    MyFirstClass inv_myFirstInstance; // no com::company:: ...
};

} // namespace package2
} // namespace company
} // namespace com

#endif /* MYSECONDCLASS_HPP_ */

MySecondClass.cpp

#include "MySecondClass.hpp"

/*
 * According to Herb Sutter's "A Good Long-Term Solution" it is fine
 * to write using declarations in a translation unit, as long as they
 * appear after all #includes.
 */
using com::company::package2::MySecondClass; // OK because in cpp file and
                                             // no more #includes following
MySecondClass::MySecondClass()
{
}

MySecondClass::~MySecondClass()
{
}

/*
 * As we have already imported all types through the class scoped typedefs
 * in our header file, we are now able to simply reuse the typedef types
 * in the translation unit as well. This pattern shortens all type names
 * down to a maximum of "ClassName::TypedefTypeName" in the translation unit -
 * e.g. below we can simply write "MySecondClass::String". At the same time the
 * class declaration in the header file now governs all type imports from other
 * namespaces which again enforces the DRY - Don't Repeat Yourself - principle.
 */

// Simply reuse typedefs from MySecondClass
MySecondClass::String MySecondClass::getText() const
{
    return this->is_text;
}

// Simply reuse typedefs from MySecondClass
void MySecondClass::setText(String as_text)
{
    this->is_text = as_text;
}

// Simply reuse typedefs from MySecondClass
void MySecondClass::setMyFirstInstance(MyFirstClass anv_instance)
{
    this->inv_myFirstInstance = anv_instance;
}

// Simply reuse typedefs from MySecondClass
MySecondClass::MyFirstClass MySecondClass::getMyFirstInstance() const
{
    return this->inv_myFirstInstance;
}

Main.cpp

#include <cstdio>
#include "MySecondClass.hpp"

using com::company::package2::MySecondClass; // OK because in cpp file and
                                             // no more #includes following
int main()
{
    // Again MySecondClass provides all types which are imported from
    // other namespaces and are part of its interface through public
    // class scoped typedefs
    MySecondClass *lpnv_mySecCls = new MySecondClass();

    // Again simply reuse typedefs from MySecondClass
    MySecondClass::String ls_text = "Hello World!";
    MySecondClass::MyFirstClass *lpnv_myFirClsf =
            new MySecondClass::MyFirstClass();

    lpnv_mySecCls->setMyFirstInstance(*lpnv_myFirClsf);

    lpnv_mySecCls->setText(ls_text);
    printf("Greetings: %s\n", lpnv_mySecCls->getText().c_str());

    lpnv_mySecCls->setText("Goodbye World!");
    printf("Greetings: %s\n", lpnv_mySecCls->getText().c_str());

    getchar();

    delete lpnv_myFirClsf;
    delete lpnv_mySecCls;

    return 0;
}
4

1 に答える 1

17

複雑さを軽減することで痛みを軽減します。あなたはC++をJavaに曲げています。(これは、他の方法を試すのと同じくらいうまくいきません。)

いくつかのヒント:

  • 「com」名前空間レベルを削除します。(これはあなたが必要としない単なるJava主義です)
  • 「company」名前空間を削除し、「product」または「library」名前空間(つまり、boost、Qt、OSGなど)に置き換えます。使用している他のライブラリとは異なるものを選択するだけです。
  • 現在の名前空間と同じ名前空間にある名前を完全に宣言する必要はありません(注意:テンプレートクラス、コメントを参照)。using namespaceヘッダー内のディレクティブは避けてください。(C ++ファイルでは、使用する場合は注意して使用してください。内部関数を使用することをお勧めします。)
  • (functions / cppファイル内の)名前空間エイリアスを検討してくださいnamespace bll = boost::lambda;。これにより、非常にきれいなショートカットが作成されます。
  • また、pimplパターンを使用してプライベートメンバー/タイプを非表示にすることで、ヘッダーに公開するタイプが少なくなります。

PS:@KillianDSのおかげで、コメントのいくつかの良いヒントがあります(質問に編集したときに削除されました)。

于 2013-01-25T20:53:43.837 に答える