2015-05-09 6 views
5

mi chiedevo se ci fosse una dichiarazione new come in C# per C++Esiste un C++ nuova dichiarazione come in C#

C# ti permette di fare questo e neatens appena il codice un po ':

FuncCall(new Foo() { 
    Bar = "sausage", 
    Boo = 4 
}); 

E 'solo ho pensato che questo era un po' sciatto in C++:

unique_ptr<Foo> foo(new Foo()); 
foo.Bar = "sausage"; 
foo.Boo = 4; 

FuncCall(move(foo)); 

Foo potrebbe essere simile a questo:

0.123.
class Foo 
{ 
public: 
    Foo(); 

    string Bar; 
    int Boo; 
} 

Perché non sto solo inserendo tutti i parametri in costruzione?

perché è stupido quando si deve inizializzare così tanto:

Foo(int width, int height, string title, string className, string thjis, stihjrjoifger gfirejgoirejgioerjgoire) Si va avanti per sempre ... mentre io già le proprietà all'interno della mia classe ... Quindi stavo chiedendo se si poteva fare.

+1

insieme con l'inizializzazione uniforme. Dovresti esaminare anche [std :: make_unique] (http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique). 'new' è usato raramente nel moderno codice C++. –

+0

È fantastico, ma non ho a disposizione C++ 14 per usarlo nella mia app attuale. Tuttavia, mi piacerebbe sapere se C++ 14 offre ciò che ho richiesto :) – Jimmyt1988

+0

_ "Foo potrebbe assomigliare a questo" _ Questo è un modo piuttosto cattivo per guardare. Solo un costruttore predefinito? Come vengono inizializzati quei membri? A cosa? Come funziona l'utente della tua classe? –

risposta

7

Si potrebbe utilizzare un lambda:

FuncCall([]{ Foo f; f.Bar = "sausage"; f.Boo = 4; return f; }()); 

Live example

+0

Foo non ha parametri costruttore. – Jimmyt1988

+0

Questo è molto più bello in realtà! non devo muovere quella merda intatta che funziona allora ... hmm hmmm yum yum yum. Lamdba in C++ sembra aver salvato la giornata qui, odio spostare tutte queste proprietà anche se sono state dichiarate appena al di sopra .. cool ... un sacco di margini di miglioramento ma ti segnerò come risposta ora. – Jimmyt1988

+0

Lo prendo un compilatore intelligente (leggi: gcc, clang, ecc.) Eliminerebbe la chiamata lambda e la inline semplicemente al momento della compilazione? –

-4

È possibile utilizzare l'inizializzazione uniforme per inizializzarlo.

+2

downvote per risposta lazy – kfsone

+0

Inoltre, non risponde alla domanda. Non sta chiedendo di usare {} s che sta chiedendo argomenti opzionali a un ctor. – kfsone

2

Foo deve avere un costruttore o di essere un tipo di aggregazione:

class Foo { 
    std::string bar; 
    int boo; 
public: 
    Foo(std::string s, int i) : bar(std::move(s)), boo(i) {} 
}; 

void FuncCall(std::unique_ptr<Foo> ptr) {} 

int main() { 
    FuncCall(make_unique<Foo>("sausage", 4)); 
} 

struct Foo { 
    std::string bar; 
    int boo; 
}; 

void FuncCall(std::unique_ptr<Foo> ptr) {} 

int main() { 
    std::unique_ptr<Foo> foo(new Foo{"sausage", 4}); 
    FuncCall(foo); 
} 

Oppure si può evitare puntatori:

void FuncCall(Foo foo) {} 

int main() { 
    FuncCall({sausage", 4}); 
} 
+0

Non è che io lo faccia in modo diverso da quello che hai qui, ma volevo solo commentare chiedendomi ad alta voce se potresti inavvertitamente promuovere miti su 'struct' in C++ ...? –

+0

Questo è utile! Grazie mille. Immagino che dovrò limitarmi a definire una massiccia e massiccia lista di paramter per tutti i paramter che voglio inizializzare ... oh, la gioia. – Jimmyt1988

+1

@LightnessRacesinOrbit Forse. Quindi, per essere chiari per i lettori: 'struct' e' class' sono esattamente gli stessi eccetto per l'accessibilità predefinita di membri e basi: 'struct's sono pubblici per impostazione predefinita. Scelgo tra i due semplicemente sulla base della riduzione al minimo del numero di specificatori espliciti di accessibilità. Alcuni standard di codifica obbligano che dovrebbero essere usati in casi d'uso specifici, come 'use 'struct' per tipi aggregati, altrimenti usare' class'.' – bames53

0

essere consapevoli che il seguente:

class Foo { 
    std::string m_bar = "Bar"; 
    int m_baz = 3; 
    float m_boo = 4.2; 
    /* ... */ 
public: 
    Foo() {} // or Foo() = default or elision 
}; 

int main() { 
    Foo f; 
    f.m_bar = "wunderBar"; 
} 

espande per essere sulla falsariga di quanto segue:

Foo* fptr = stack_allocate<Foo*>(sizeof(Foo)); 
// from ctor 
fptr->m_bar.string("Bar"); // construct m_bar 
fptr->m_baz.int(3); 
fptr->m_boo.float(4.2); 
// your code: 
fptr->m_bar.operator=("wunderBar"); 

Per ragioni simili, si potrebbe desiderare di guarda le istruzioni IL per il tuo costrutto C# - scoprirai che sta eseguendo operazioni ugualmente ridondanti (e in situazioni più complesse, possibilmente box/unboxing).

Il tuo approccio C++ ti mancherà anche quando incorporerai tipi non copiabili o non mobili che ti obbligheranno a passare puntatori e/o piegare il tuo progetto.

Cosa/sembra/si sta cercando di fare è ricreare parametri opzionali di Python:

# C++-alike 
class Foo(object): 
    def __init__(self, Bar, Baz, Boo): 
     ... 

# C#-alike: 
class Foo(object): 
    def __init__(self, Bar="Bar", Baz=13, Boo=4.2): 
     ... 

C++ non fornisce un modo diretto di fare questo, i meccanismi più vicini sono i parametri di default e overloading degli operatori:

class Foo { 
    std::string m_bar = "Bar"; 
    int m_baz = 3; 
    float m_boo = 4.2; 
public: 
    Foo(std::string bar="Bar", int baz=3, int boo=6.1) 
     : m_bar(bar), m_baz(baz), m_boo(boo) 
     {} 
    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=13, boo=6.1); 
    * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2); 
    * Foo* f = new Foo("hello", 1, 1.); => new Foo(bar="hello", baz=1, boo=1.); 
    * Foo* f = new Foo(42.); => invalid, arguments must be in order. 
    */  
}; 

o

class Foo { 
    std::string m_bar = "Bar"; 
    int m_baz = 3; 
    float m_boo = 4.2; 
public: 
    Foo() = default; 

    // allow Foo("hello") 
    Foo(const char* bar) : m_bar(bar) {} 
    Foo(const std::string& bar) : m_bar(bar) {} 
    Foo(std::string&& bar) : m_bar(std::forward(bar)) {} 

    // allow Foo(10, 12.) 
    explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {} 

    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=3, boo=4.2); 
    * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2); 
    * Foo* f = new Foo(1, 1.); => new Foo(bar="Bar", baz=1, boo=1.); 
    * Foo* f = new Foo(42.); => invalid, no match 
    */  
}; 

See http://ideone.com/yFIqlA per SSCE.

Se si dispone effettivamente di una dozzina di diverse configurazioni del costruttore, è probabile che si debba riconsiderare il progetto.

--- --- Modifica

Nota: Non è obbligatorio che si espone tutti i parametri nel costruttore:

class Foo { 
    std::string m_user_supplied; 
    std::time_t m_time; 
public: 
    Foo() : m_user_supplied(), m_time(0) {} 
    Foo(std::string src) : m_user_supplied(src), m_time(0) {} 
    void addTime(time_t inc) { m_time += inc; } 
}; 

--- --- Modifica 2

"dovrebbe forse ripensare il tuo design "... Un problema con elenchi di parametri grandi e opzionali è la crescita. È probabile che si finisca con parametri che dipendono l'uno dall'altro, che si contraddicono o interagiscono tra loro. È possibile scegliere di non convalidare questi o si può finire con costruttori complicati.

struct Foo { 
    ... 
    FILE* m_output; 
    const char* m_mode; 
    ... 

    Foo(..., FILE* output, const char* mode, ...) 
    { 
     ... 
     if (output != nullptr) { 
      ASSERT(output == nullptr || mode != nullptr); 
      ... other requirements 
     } else { 
      if (mode != nullptr) 
       ... might not be an error but it might be a bug ... 
     } 
     ... 
    } 
}; 

Un approccio per evitare questo è l'utilizzo di incapsulamento/aggregazione di membri correlati.

class Foo { 
    ... 
    struct FileAccess { 
     FILE* m_file; 
     const char* m_mode; 
     constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {} 
     FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) { 
      if (file == nullptr || mode == nullptr) 
       throw invalid_argument("file/mode cannot be null"); 
     } 
    }; 
    ... 

    FileAccess m_access; 

    Foo(..., FileAccess access, ...); 
}; 

Questo può essere un modo giusto per ridurre il rigonfiamento. Se la vostra API è stabile, è possibile utilizzarlo con le liste di inizializzazione (se la vostra API non è stabile e si modifica ti morderà nel culo)

auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...}; 
auto fooWithout = make_unique<Foo>{..., /*access=*/{}, ...}; 

Se successivamente si decide di smettere di usare ctors e passare a utilizzare setter, questo si tradurrà ragionevolmente bene, dato che è possibile avere sovraccaricato "set" che prende uno dei vari struct di configurazione:

auto foo = make_unique<Foo>(); 
foo->set(FileAccess(stdout, "w")) 
    ->set(Position(Right, -100, Top, 180)) 
    ->set(DimensionPercentage(80, 75)); 

vs

auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax 
    m_file = stdout; 
    m_mode = "w"; 
    m_xPosition = -100; 
    m_xPositionRel = Right; 
    m_yPosition = -180; 
    m_yPositionRel = Top; 
    m_dimensionType = Percentage; 
    m_xDimension = 80; 
    m_yDimension = 75; 
}; 
+0

"ripensare il tuo design", non sono sicuro che sia necessario ... specialmente quando tutti questi parametri sono collegati alla costruzione del Foo (o nella mia vera classe, è una finestra) ... Ho altezza, larghezza , titolo, classname, handletowindow ... ma credo che sia bello condividere la tua opinione. – Jimmyt1988

+0

"dovrebbe probabilmente". Inoltre intendo "ripensare" letteralmente, non come "ricominciare". IME un "ripensamento" può arrivare a un design simile con piccole differenze. Ho lavorato con diversi progetti che abbracciavano l'approccio "big struct, fill out what you need" e ha molti vantaggi in crescita, come impostazioni contrastanti e/o valori interdipendenti ("baz" significa nulla a meno che tu imposta 'boo'; se 'parent' è impostato, devi specificare una 'modalità'). A quel punto, potrebbe essere meglio rendere banale il ctor predefinito e usare setter per tutto. O utilizzare i parametri aggregati. – kfsone

+0

Vedere la modifica n. 2 per una spiegazione di quanto sopra. – kfsone