2016-03-30 12 views
7

che stavo cercando di capire l'utilizzo di parole chiave esplicita in C++ e guardò questa domanda su SO What does the explicit keyword mean in C++?utilizzo di parole chiave esplicita per i costruttori

Tuttavia, esempi elencati lì (in realtà entrambe le prime due risposte) non sono molto chiare per quanto riguarda l'utilizzo. Ad esempio,

// classes example 
#include <iostream> 
using namespace std; 

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

String::String(int n) 
{ 
    cout<<"Entered int section"; 
} 

String::String(const char *p) 
{ 
    cout<<"Entered char section"; 
} 

int main() { 

    String mystring('x'); 
    return 0; 
} 

Ora ho dichiarato String costruttore come esplicita, ma consente di dire se io non lista come esplicita, se io chiamo il costruttore come,

String mystring('x'); 

O

String mystring = 'x'; 

In entrambi i casi, entrerò nella sezione int. Fino a quando non specificherò il tipo di valore, per impostazione predefinita int. Anche se ho più specifica con argomenti, come dichiara uno come int, altri come doppio, e non uso esplicito con il nome del costruttore e chiamare in questo modo

String mystring(2.5); 

O questo modo

String mystring = 2.5; 

Sarà sempre predefinito per il costruttore con il doppio come argomento. Quindi, sto avendo difficoltà a capire l'uso reale di esplicito. Potete fornirmi un esempio in cui non usare esplicitamente sarà un vero fallimento?

+1

Ti aspetti 'String ('x')' per chiamare 'String (const char *)'? ''x'' è un' char', non un puntatore 'char'. Ad ogni modo, stai mostrando solo costruzioni * esplicite * di 'String', quindi' explicit' non fa differenza. Prova a chiamare una funzione che accetta un 'String' con un' int', e non funzionerà, perché la conversione 'int' →' String' non può essere resa * implicitamente *. – Biffen

+0

'String mystring = 'x';' [fa _non_ work] (http://coliru.stacked-crooked.com/a/79af8ce27adf8a61). – cpplearner

+0

@cpplearner Sono consapevole di ciò, ecco perché ho indicato che non uso la parola esplicita. Nel tuo esempio, stai usando esplicito –

risposta

6

explicit ha lo scopo di impedire conversioni implicite. Ogni volta che si utilizza qualcosa come String(foo);, si tratta di una conversione esplicita, quindi l'utilizzo di explicit non cambierà in caso di esito positivo o negativo.

Pertanto, diamo un'occhiata a uno scenario che comporta la conversione implicita. Cominciamo con la tua String classe:

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

Poi definiamo una funzione che riceve un parametro di tipo String (potrebbe anche essere String const &, ma String farà per il momento):

int f(String); 

I suoi costruttori consentire la conversione implicita da char const *, ma solo la conversione esplicita da int. Ciò significa che se chiamo:

f("this is a string"); 

... il compilatore genererà il codice per costruire un oggetto stringa dalla stringa letterale, e poi chiamo f con quella String oggetto.

Se, tuttavia, si tenta di chiamare:

f(2); 

fallirà, poiché il costruttore String che accetta un parametro int è stata segnata explicit.Ciò significa che se voglio convertire un int ad un String, devo farlo in modo esplicito:

f(String(2)); 

Se il costruttore String(char const *); furono segnati anche explicit, allora non sarebbe in grado di chiamare f("this is a string") sia - si 'd devono utilizzare f(String("this is a string"));

si noti, tuttavia, che controlla solo explicit conversione implicita da un certo tipo foo al tipo definito. Non ha alcun effetto sulla conversione implicita da altro tipo a il tipo utilizzato dal costruttore explicit. Quindi, il tuo esplicito costruttore che prende digitare int vorrà ancora un parametro in virgola mobile:

f(String(1.2)) 

... perché questo comporta una conversione implicita da double a int seguita da una conversione esplicita da int a String. Se si vuole impedire una conversione double-String, lo faresti per (ad esempio) che fornisce un costruttore di overload che prende un double, ma poi getta:

String(double) { throw("Conversion from double not allowed"); } 

Ora la conversione implicita da double a int non accadrà - lo double verrà passato direttamente al tuo ctor senza conversione.

Quanto a quello che utilizza explicit compie: il punto principale di usare explicit è quello di evitare che codice da compilare che altrimenti la compilazione. Se combinato con il sovraccarico, le conversioni implicite possono portare ad alcune selezioni piuttosto strane a volte.

È più semplice dimostrare un problema con gli operatori di conversione piuttosto che con i costruttori (perché è possibile farlo solo con una classe). Per esempio, prendiamo in considerazione una piccola classe string abbastanza simile a un sacco che sono stati scritti prima che la gente intesa come molto su come problematica implicita conversioni possono essere:

class Foo { 
    std::string data; 
public: 
    Foo(char const *s) : data(s) { } 
    Foo operator+(Foo const &other) { return (data + other.data).c_str(); } 

    operator char const *() { return data.c_str(); } 
}; 

(ho tradito utilizzando std::string per memorizzare i dati, ma lo stesso sarebbe vero se avessi fatto come loro e memorizzato un char *, e usato new per allocare la memoria).

Ora, questo rende le cose come queste funzionano bene:

Foo a("a"); 
Foo b("b"); 

std::cout << a + b; 

... e, (ovviamente) il risultato è che esso stampa ab. Ma cosa succede se l'utente commette un errore minore e digita - dove intendeva digitare +?

Ecco dove le cose si fanno brutte - il codice è ancora compilato e "funziona" (per qualche definizione della parola), ma stampa assurdità. In un rapido test sulla mia macchina, ho ottenuto -24, ma non contano di duplicare quel particolare risultato.

Il problema qui deriva dal consentire la conversione implicita da String a char *. Quando proviamo a sottrarre i due oggetti String, il compilatore prova a capire cosa intendessimo.Poiché non è in grado di sottrarli direttamente, verifica se è possibile convertirli in un tipo che supporta la sottrazione e, in effetti, char const * supporta la sottrazione, quindi converte entrambi i nostri oggetti String in char const *, quindi sottrae i due puntatori.

Se vi segnalo che la conversione come explicit invece:

explicit operator char const *() { return data.c_str(); } 

... il codice che tenta di sottrarre due String oggetti semplicemente non verrà compilato.

La stessa idea di base può/si applica ai costruttori explicit, ma il codice per dimostrarlo si allunga perché generalmente sono necessari almeno un paio di classi diverse.

+0

Non sono sicuro che tu abbia corretto la conversione da dubbio a int. Se passo un doppio valore all'oggetto String, andrà direttamente al costruttore che si aspetta il doppio come argomento (se ce n'è uno), indipendentemente dall'esplicito. –

+0

@UnderDog: Sì e no. Sì, è quella che verrà selezionata la risoluzione di sovraccarico. Tuttavia, se è contrassegnato come esplicito e stai tentando di utilizzarlo per eseguire una conversione implicita, il codice non verrà compilato. –

+0

Lo capisco e sono d'accordo. Ma la mia domanda non riguarda questo. Sono più interessato a conoscere i casi o un codice di esempio, in cui non utilizzando cause esplicite un fallimento o un comportamento indesiderato, perché finora con la spiegazione che ho capito, non vedo molto la pena di usare esplicito. –

Problemi correlati