Prefazione:C++ 11 Elimina sovrascritti Metodo
Questa è una domanda sulle migliori pratiche per quanto riguarda un nuovo significato del operatore delete introdotta con C++ 11 quando applicate a una classe figlia override di un genitore virtuale ereditato metodo.
Background:
Per lo standard, il primo caso d'uso citato è quello di non consentire in modo esplicito le funzioni di chiamata per alcune tipologie in cui le conversioni che altrimenti sarebbe implicita come l'esempio da §8.4.3 delle ultime C++11 standard draft:
struct sometype {
sometype() = delete; // OK, but redundant
some_type(std::intmax_t) = delete;
some_type(double);
};
L'esempio precedente è chiaro e mirato. Tuttavia, il seguente esempio in cui il nuovo operatore è stato sovrascritto e impedito di essere chiamato definendolo come eliminato, mi ha iniziato a pensare ad altri scenari che identificherei successivamente nella sezione domanda (l'esempio di seguito è dal §8.4.3 dello C++11 standard draft):
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
domanda:
per estensione di questo pensiero per eredità, sono curioso di di altri pensieri per quanto riguarda se il seguente esempio di utilizzo è un caso d'uso chiaro e valido o se si tratta di un abuso chiaro della funzionalità appena aggiunta. Fornire una giustificazione per la risposta (sarà accettato l'esempio che fornisce il ragionamento più convincente). Nell'esempio che segue, il design tenta di mantenere due versioni della libreria (la libreria deve essere istanziata) avendo la seconda versione della libreria ereditata dalla prima. L'idea è di consentire correzioni di bug o modifiche apportate alla prima versione della libreria per propagarsi automaticamente alla seconda versione della libreria, consentendo al contempo alla seconda versione della libreria di concentrarsi solo sulle sue differenze rispetto alla prima versione. Per deprecare una funzione nella seconda versione della libreria, l'operatore delete viene utilizzato per non consentire una chiamata alla funzione di override:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something else */ }
};
class LibraryVersion2 : public LibraryVersion1 {
public:
// Deprecate the doSomething1 method by disallowing it from being called
virtual void doSomething1() override = delete;
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
Anche se posso vedere molti benefici ad un tale approccio, penso che tendo più verso il pensiero che si tratta di un abuso della funzionalità. La trappola primaria che vedo nell'esempio precedente è che la classica relazione "is-a" dell'eredità è rotta. Ho letto molti articoli che raccomandano fortemente contro qualsiasi uso dell'ereditarietà per esprimere una relazione "sorta di un è un" e utilizzare invece la composizione con le funzioni wrapper per identificare chiaramente le relazioni delle classi. Mentre il seguente esempio, spesso accigliato, richiede uno sforzo maggiore per implementare e mantenere (per quanto riguarda il numero di righe scritte per questo pezzo di codice, poiché ogni funzione ereditata per essere disponibile pubblicamente deve essere esplicitamente richiamata dalla classe ereditaria), l'uso di Delete come illustrato sopra è molto simile in molti modi:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
virtual void doSomething2() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something */ }
};
class LibraryVersion2 : private LibraryVersion1 {
// doSomething1 is inherited privately so other classes cannot call it
public:
// Explicitly state which functions have not been deprecated
using LibraryVersion1::doSomething2();
// ... using (many other library methods)
using LibraryVersion1::doSomethingN();
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
Grazie in anticipo per le vostre risposte e ulteriori indicazioni circa l'uso di questo potenziale caso di cancellazione.
Non penso che 'virtual void doSomething1() override = delete;' sia legale. Cosa vorresti? ((LibraryVersion1 *) (new LibraryVersion2)) -> doSomething1() 'do? –
Per quanto ne so, anche con il cast su LibraryVersion1, la funzione eliminata avrebbe comunque tentato di essere chiamata dato l'override di LibraryVersion2 e il codice non riusciva a compilare. Questo è il punto in cui la relazione "è-a" è interrotta, come indicato nella mia domanda, ma sicuramente imporrebbe la deprecazione come previsto. – statueuphemism
fresh: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf – qPCR4vir