c++ schlüsselwörter - Was bedeutet das explizite Keyword?




bedeutung keywords (10)

Cpp Reference ist immer hilfreich !!! Details zum expliziten Spezifizierer finden Sie here . Möglicherweise müssen Sie auch implizite Konvertierungen und copy-initialization .

Schneller Blick

Der explizite Bezeichner gibt an, dass ein Konstruktor oder eine Konvertierungsfunktion (seit C ++ 11) keine impliziten Konvertierungen oder keine Kopierinitialisierung zulässt.

Beispiel wie folgt:

struct A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
    operator bool() const { return true; }
};

struct B
{
    explicit B(int) { }
    explicit B(int, int) { }
    explicit operator bool() const { return true; }
};

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast
    if (a1) cout << "true" << endl; // OK: A::operator bool()
    bool na1 = a1; // OK: copy-initialization selects A::operator bool()
    bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
    if (b5) cout << "true" << endl; // OK: B::operator bool()
//  bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
    bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
}

Was bedeutet das explicit Schlüsselwort in C ++?


In C ++ wird ein Konstruktor mit nur einem erforderlichen Parameter als implizite Konvertierungsfunktion betrachtet. Es konvertiert den Parametertyp in den Klassentyp. Ob dies eine gute Sache ist oder nicht, hängt von der Semantik des Konstruktors ab.

Wenn Sie beispielsweise eine String-Klasse mit dem Konstruktor String(const char* s) haben, ist dies wahrscheinlich genau das, was Sie möchten. Sie können ein const char* an eine Funktion übergeben, die einen String erwartet, und der Compiler erstellt automatisch ein temporäres String Objekt für Sie.

Wenn Sie dagegen über eine Pufferklasse verfügen, deren Konstruktorpuffer Buffer(int size) die Größe des Puffers in Bytes annimmt, möchten Sie wahrscheinlich nicht, dass der Compiler int s in Buffer s verwandelt. Um dies zu verhindern, deklarieren Sie den Konstruktor mit dem explicit Schlüsselwort:

class Buffer { explicit Buffer(int size); ... }

Dieser Weg,

void useBuffer(Buffer& buf);
useBuffer(4);

wird zu einem Fehler bei der Kompilierung. Wenn Sie ein temporäres Buffer möchten, müssen Sie dies explizit tun:

useBuffer(Buffer(4));

Zusammenfassend: Wenn Ihr Konstruktor mit einem einzigen Parameter den Parameter in ein Objekt Ihrer Klasse konvertiert, möchten Sie wahrscheinlich nicht das explicit Schlüsselwort verwenden. Wenn Sie jedoch einen Konstruktor haben, der nur einen einzigen Parameter verwendet, sollten Sie ihn als explicit deklarieren, um zu verhindern, dass der Compiler Sie mit unerwarteten Konvertierungen überrascht.


Konstruktoren hängen eine implizite Konvertierung an. Um diese implizite Konvertierung zu unterdrücken, muss ein Konstruktor mit einem expliziten Parameter deklariert werden.

In C ++ 11 können Sie auch einen "operator type ()" mit dem Schlüsselwort here angeben. Mit dieser Angabe können Sie operator für explizite Konvertierungen verwenden, und direkte Initialisierung des Objekts.

PS Bei Verwendung der von USER definierten Transformationen (über Konstruktoren und Typkonvertierungsoperator) ist nur eine Ebene der impliziten Konvertierungen zulässig. Sie können diese Konvertierungen jedoch mit anderen Sprachkonvertierungen kombinieren

  • ganzzahlige Ränge (char bis int, float bis double);
  • Standart-Konvertierungen (int in double);
  • Zeiger von Objekten in Basisklasse und in Void * umwandeln;

Dies wurde bereits diskutiert ( was ist der explizite Konstruktor ). Aber ich muss sagen, dass es die detaillierten Beschreibungen hier fehlt.

Außerdem ist es immer eine gute Kodierungspraxis, wie Sie bereits einen Argumentkonstruktor (einschließlich solcher mit Standardwerten für arg2, arg3, ...) erstellt haben. Wie immer bei C ++: Wenn Sie dies nicht tun, wünschen Sie das auch ...

Eine weitere bewährte Methode für Klassen besteht darin, die Erstellung und Zuweisung von Kopien als privat zu definieren (dh deaktivieren Sie sie), sofern Sie sie nicht wirklich implementieren müssen. Dadurch wird vermieden, dass bei Verwendung der Methoden, die C ++ standardmäßig für Sie erstellt, möglicherweise Zeigerkopien verwendet werden. Ein anderer Weg, dies zu tun, ist von boost :: noncopyable.


Das explicit -keyword kann verwendet werden, um den explicit Aufruf eines Konstruktors zu erzwingen.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

Das explicit -keyword vor dem Konstruktor C(void) teilt dem Compiler mit, dass nur ein expliziter Aufruf dieses Konstruktors zulässig ist.

Das explicit -keyword kann auch in benutzerdefinierten Typumwandlungsoperatoren verwendet werden:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

Hier erzwingt explicit -keyword nur explicit Umsetzungen, um gültig zu sein, sodass bool b = c; wäre in diesem Fall eine ungültige Besetzung. In Situationen wie diesen kann das explicit -keyword Programmierern dabei helfen, implizite, unbeabsichtigte Casts zu vermeiden. Diese Verwendung wurde in C++11 standardisiert.


Das explicit Schlüsselwort macht einen Konvertierungskonstruktor zu einem Nichtkonvertierungskonstruktor. Dadurch ist der Code weniger fehleranfällig.


Angenommen, Sie haben eine Klasse String :

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

Nun, wenn Sie versuchen:

String mystring = 'x';

Das Zeichen 'x' wird implizit in int konvertiert und der String(int) -Konstruktor wird aufgerufen. Dies ist jedoch nicht das, was der Benutzer beabsichtigt hat. Um solche Bedingungen zu vermeiden, definieren wir den Konstruktor als explicit :

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

Bei dieser Antwort geht es um die Objekterstellung mit / ohne expliziten Konstruktor, da sie in den anderen Antworten nicht behandelt wird.

Betrachten Sie die folgende Klasse ohne expliziten Konstruktor:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

Objekte der Klasse Foo können auf zwei Arten erstellt werden:

Foo bar1(10);

Foo bar2 = 20;

Je nach Implementierung kann die zweite Art der Instanziierung der Klasse Foo verwirrend sein oder nicht, was der Programmierer beabsichtigt hat. Wenn Sie das explicit Schlüsselwort dem Konstruktor Foo bar2 = 20; wird bei Foo bar2 = 20; ein Compilerfehler generiert Foo bar2 = 20; .

Normalerweise empfiehlt es sich, Konstruktoren mit einem Argument als explicit zu deklarieren, es sei denn, Ihre Implementierung verbietet dies explicit .

Beachten Sie auch, dass Konstruktoren mit

  • Standardargumente für alle Parameter oder
  • Standardargumente ab dem zweiten Parameter

können beide als Konstrukteure mit einem Argument verwendet werden. Daher möchten Sie diese auch explicit .

Ein Beispiel, in dem Sie Ihren Konstruktor mit einem einzigen Argument absichtlich nicht explizit machen möchten, ist das Erstellen eines Funktors (siehe die in this Antwort deklarierte Struktur 'add_x'). In einem solchen Fall erstellen Sie ein Objekt als add_x add30 = 30; wäre wahrscheinlich sinnvoll.

Here ist ein guter Bericht über explizite Konstruktoren.


Der Compiler darf eine implizite Konvertierung durchführen, um die Parameter in eine Funktion aufzulösen. Dies bedeutet, dass der Compiler Konstruktoren verwenden kann, die mit einem einzigen Parameter aufgerufen werden können , um von einem Typ in einen anderen zu konvertieren, um den richtigen Typ für einen Parameter zu erhalten.

Hier ist eine Beispielklasse mit einem Konstruktor, der für implizite Konvertierungen verwendet werden kann:

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

Hier ist eine einfache Funktion, die ein Foo Objekt übernimmt:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

und hier wird die DoBar Funktion aufgerufen.

int main ()
{
  DoBar (42);
}

Das Argument ist kein Foo Objekt, sondern ein int . Es gibt jedoch einen Konstruktor für Foo , der ein int sodass dieser Konstruktor verwendet werden kann, um den Parameter in den richtigen Typ zu konvertieren.

Der Compiler darf dies einmal für jeden Parameter.

Wenn Sie dem Konstruktor das explicit Schlüsselwort voranstellen, kann der Compiler diesen Konstruktor nicht für implizite Konvertierungen verwenden. Wenn Sie es zur obigen Klasse DoBar (42) wird beim Funktionsaufruf DoBar (42) ein Compilerfehler DoBar (42) . Es ist jetzt notwendig, die Konvertierung explizit mit DoBar (Foo (42))

Möglicherweise möchten Sie dies tun, um versehentliche Konstruktionen zu vermeiden, die Fehler verbergen können. Gedachtes Beispiel:

  • Sie haben eine MyString(int size) Klasse MyString(int size) mit einem Konstruktor, der eine Zeichenfolge der angegebenen Größe erstellt. Sie haben eine Funktion print(const MyString&) und rufen print(3) (wenn Sie eigentlich print("3") aufrufen wollten). Sie erwarten, dass es "3" druckt, aber es gibt stattdessen eine leere Zeichenfolge der Länge 3 aus.

Zusätzlich zu den anderen bisherigen Antworten gibt es hier ein nicht offensichtliches Beispiel, bei dem static_cast nicht ausreicht, um reinterpret_cast benötigen. Angenommen, es gibt eine Funktion, die in einem Ausgabeparameter Zeiger auf Objekte verschiedener Klassen (die keine gemeinsame Basisklasse haben) zurückgibt. Ein reales Beispiel für eine solche Funktion ist CoCreateInstance() (siehe den letzten Parameter, der in Wirklichkeit void** ). Angenommen, Sie fordern eine bestimmte Objektklasse von dieser Funktion an, so dass Sie den Typ des Zeigers im Voraus wissen (was Sie häufig für COM-Objekte tun). In diesem Fall können Sie mit static_cast keinen Zeiger auf Ihren Zeiger in void** static_cast : Sie benötigen reinterpret_cast<void**>(&yourPointer) .

In Code:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

static_cast funktioniert jedoch für einfache Zeiger (nicht für Zeiger auf Zeiger), sodass der obige Code umgeschrieben werden kann, um reinterpret_cast (zum Preis einer zusätzlichen Variablen) folgendermaßen zu vermeiden:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);




c++ constructor explicit c++-faq explicit-constructor