Wie überwinden Sie den namespace böse von C++ - header-Dateien?

Mit einem meiner Projekte will ich den Kopf in den C++ Bereich. Grundsätzlich bin ich kommen
aus einem Java-hintergrund und Frage mich, wie das Konzept der Java-Pakete
realisiert sich in der C++ Welt. Dies führte mich zu der C++ - Konzept der namespaces.

Ich bin vollkommen zufrieden mit namespaces, so weit, aber wenn es um die header-Dateien
die Dinge sind immer irgendwie ineffizient mit Bezug auf voll qualifizierte Klasse
Namen, using-Direktiven und using-Deklarationen.

Eine sehr gute Beschreibung des Problems ist diese Artikel von Herb Sutter.

Wie ich es verstehe, das kommt darauf an: Wenn Sie schreiben Sie eine header-Datei immer
mit vollständig qualifizierten Typnamen beziehen sich auf Typen aus anderen Namensräumen.

Das ist fast inakzeptabel. Als C++ - header-allgemein liefert die Erklärung
einer Klasse, maximal Lesbarkeit hat oberste Priorität. Voll qualifizierende jeder
geben Sie von einem anderen namespace erstellt eine Menge von visuellen Lärm, schließlich
abnehmende Lesbarkeit des headers zu einem gewissen Grad, was die Frage aufwirft,
ob namespaces überhaupt.

Dennoch möchte ich um die Vorteile von C++ namespaces und so setzen einige Gedanken in
die Frage: Wie überwinden Sie den namespace böse von C++ - header-Dateien? Nach
einige der Forschung, ich denke, typedefs konnte ein gültiges Heilmittel für dieses problem.

Folgenden finden Sie ein C++ Beispielprogramm, das veranschaulicht, wie würde ich
wie zu verwenden public class Gültigkeitsbereich typedefs zu importieren, Typen aus anderen Namensräumen.
Das Programm ist syntaktisch korrekt und kompiliert problemlos auf MinGW-W64. So weit, So
gut, aber ich bin mir nicht sicher, ob dieser Ansatz glücklich beseitigt die Verwendung von keyword
aus dem Kopf-aber bringt ein anderes problem, das bin ich einfach nicht bewusst.
Nur etwas tricky, wie die Dinge beschrieben, von Herb Sutter.

Dass ist ich bitte alle, die ein gründliches Verständnis von C++
überprüfen Sie den code unten und lassen Sie mich wissen, ob dies funktionieren sollte, oder nicht. Dank
für Ihre Gedanken.

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;
}
  • Dies ist nicht wirklich eine Frage. Vielleicht sollte man post-code-review?
  • Verwenden Sie die vollständig qualifizierten Namen und erstellen Sie keine Java-like namespace-Namen. Verwenden Sie nicht new. Willkommen zu C++.
  • Dies werden die wenigsten sorgen, in C++, nur geben Sie es einige Zeit
  • nur nicht so viele Level-namespace, ich habe selten einen tatsächlichen Bedarf darüber hinaus einer Ebene. die com::company Teil ist vor allem sinnlos.
  • Vergessen Sie, was Sie wissen über Java. Nicht nest namespaces so viel. Verwenden Sie nicht new wenn Sie es nicht benötigen (in diesem Beispiel nicht benötigen). Versuch C++ zu schreiben, als ob es Java wird nicht gute Ergebnisse erzielen. Behaupte nicht, die Sprachen sind ähnlich, denn das sind Sie nicht.
  • Ich finde, std::string wesentlich schneller einzutippen und einfacher zu Lesen als MySecondClass::String. Über Ihren code, den übermäßigen Einsatz von leer-namespaces Nester ist ein problem. Nenne es einfach package1, drop das gesamte Unternehmen Sachen. Schließlich, eine typische Java-Fehler: Wenn Sie neu in C++ um das gleiche zu erreichen wie in Java, dann machst du etwas falsch. Viel Glück!
  • Vermeiden Sie auch die Ungarische notation. Das ist für C++ - BC. 😉
  • - Klasse-level-typedefs sind ok. Namespace-Ebene nicht. Die OP ist etwas zu tun annehmbar in seinen code.
  • oh du hast Recht, falsch verstanden, es durch Einrückung Fehler, aber es ist immer noch ziemlich nutzlos und nicht hilfreich/etwas schädlich.
  • Ja. Typdefinition:ing std::string ist sinnlos und Verschleierung. Typedeffing template-Klassen und Container ist etwas, was ich nützlich finde, aber
  • Leute verwendet, um C++ oft finden voll qualifizierten Namen, die einfacher zu Lesen, da Sie nicht haben, zu erraten, was namespace aktiv ist.
  • Denke der managed C++ mit namespaces wie System::Runtime::InteropServices. Es muss für mehrere namespace-Ebenen, egal ob es Java, C++ oder jede andere Sprache. Die Tatsache ist, header-Dateien ein problem bei der Verwendung von namespaces und es gibt keine saubere Möglichkeit um ihn herum. Warum glaubst du, keine Sprachen erstellt in den letzten zwei Jahrzehnten Einsatz in den header-Dateien?
  • tun Sie es nicht, machen es erforderlich, elegant oder modern. Die meisten C++ - code auch Leben ohne es.
  • Ich würde empfehlen, die Vermeidung von using Richtlinien sowohl in den header und die Implementierung von Dateien. Insbesondere eine using-Direktive, ziehen Sie die Elemente, für die Sie gehen, um die definition werden Sie in Schwierigkeiten geraten.
  • Das problem mit header-Dateien nichts zu tun hat mit namespaces. namespaces in C++ nicht .Net-Module, so gibt es keine Notwendigkeit für die gleichen Schachteln als mit Modulen.
  • Das ist nur eine design-Entscheidung, weil Sie wollten, zu halten die gleiche Hierarchie, die in anderen .NET-Sprachen. Es gibt nichts Hemmung der Nutzer SystemRT statt System::Runtime (betrachten crt ist eine bekannte Abkürzung für C-runtime) oder auch SysRTInterop für die ganze Sache. Ich erinnere mich nicht, an jedem Ort, wo ich mehr als zwei verschachtelte namespaces (und in einigen Orten der externen war die Firma/Gruppe name)
  • Das problem der header-Dateien mit namespaces ist, dass, wie in der Frage beschriebenen, sollten Sie nicht verwenden Verwendung - Deklarationen im Header, da dann die Namen werden exportiert, um die Globale Reichweite von wer gehören der header, damit Sieg über den Zweck von namespaces. Du bist also mit der linken, die brauchen, um vollständig zu qualifizieren, alle Namen, auch wenn Sie in einem wirklich lange namespace.
  • Ich bin mir nicht sicher, was du meinst. .NET ist sprachunabhängig, aber der namespace-Hierarchie ist das gleiche in jeder Sprache, die unterstützt .NET.
  • Genau, die Hierarchie in C++ nicht für C++ verabschiedet von der .NET-framework. Eine gleichwertige framework für C++ würde befolgt haben unterschiedliche Konventionen und hätte wahrscheinlich SystemRT oder SysRTInterop eher als ausführliches System::Runtime::InteropServices

InformationsquelleAutor Thomas G. | 2013-01-25
Schreibe einen Kommentar