2013-02-19 1 views
11

La funzione di categoria Objective-C consente al programmatore di aggiungere un nuovo metodo che non è stato definito nella definizione di classe originale.Il costrutto o la tecnica di tipo Objective-C in C++?

Posso archiviare funzionalità simili (costruzione del linguaggio o tecnica) su C++?

La principale preoccupazione è la sintassi di chiamata del metodo coerente (. o -> operatore).

+0

Sì, Se si ha accesso alla definizione di classe. No altrimenti. –

+0

In fase di esecuzione non si ottiene alcun supporto linguistico per questo. Tuttavia, puoi farlo utilizzando per es. una mappa contenente puntatori di funzione. Ma questo è piuttosto ingombrante e soggetto a errori. –

+0

@AlokSave Intendi modificare direttamente il codice sorgente? Potrebbe essere un'opzione. – Eonil

risposta

7

Consideriamo la seguente classe da estendere:

struct A { 
    int x, y; 
    A(int x, int y) : x(x), y(y) {} 
}; 

È possibile ereditare da questa classe o scrivere una classe wrapper che contiene un'istanza di questa classe. Nella maggior parte dei casi, l'ereditarietà è la strada da percorrere, come una classe wrapper non è una A, ma avvolge (contiene) una A.

Con C++ 11 semantica muoversi, promuovere un'istanza A a una sottoclasse B (ereditando A) sarà efficace e non richiede di copiare l'istanza A:

class B : public A { 
public: 
    B (A &&a) : A(a), someOtherMember(a.x + a.y) {} 

    // added public stuff: 
    int someOtherFunction() const { return someOtherMember; } 

private: 
    // added private stuff: 
    int someOtherMember; 
}; 

codice completo con l'esempio: http://ideone.com/mZLLEu

Naturalmente la funzione aggiungo ed è un po 'sciocco (e il membro ancora di più, dal momento che non rispetta ulteriori cambiamenti dei membri originali x e), ma dovresti avere un'idea di ciò che voglio dimostrare.

Nota il costruttore B (A &&a) che è qualcosa che chiamo "promuovi costruttore" (questo non è un termine standard). Normalmente, move constructor, che sposta il contenuto dell'istanza B fornita in un nuovo B in procinto di essere costruito. Io uso la semantica di spostamento su sposta un'istanza di A (che è stata restituita da un'altra funzione) in la super-classe A di B.

In modo efficace, è possibile promuovere A a B rendendo possibile l'utilizzo di B come A.

In contrasto con la risposta di Soonts, la mia soluzione funziona anche con tabelle virtuali aggiunte, poiché non si basa sul casting puntatore non sicuro.

+0

Ho capito che * move ctor * crea un nuovo oggetto semanticamente, e il compilatore può riutilizzare l'oggetto esistente se possibile. E il tuo * promotore * ha anche le stesse possibilità di ottimizzazione del compilatore. La mia comprensione è corretta? – Eonil

+1

'A (A && a)' è cursore per 'A'. Questo sposterà un vecchio 'A' in un nuovo' A'. Semanticamente è un nuovo oggetto, ma grazie alla mossa semantica può "rubare" il contenuto del vecchio 'A'. L'idea del mio approccio è spostare il vecchio 'A' in un' B'. Poiché 'B' eredita da' A', deve inizializzare comunque un 'A'.Io uso il codice di spostamento di 'A' in un costruttore di' B' che voglio chiamare * promuovere ctor * (ho fatto questa parola, poiché effettivamente promuove un 'A' esistente a un' B' senza toccare il contenuto di 'A' purché il suo mittente sia ben scritto). – leemes

+0

Tuttavia, qui c'è una sottigliezza: considera il caso in cui hai anche ad esempio 'classe C: public A', con un costruttore di mosse da' A'. In tal caso, l'oggetto che hai è un 'A', un' B' o un 'C'. Non può essere sia 'B' che' C' allo stesso tempo. – alastair

1

C++ ha l'ereditarietà per quello. Inoltre, ho usato il seguente trucco un paio di volte a estendere le classi generate da #import "progid: ..." direttiva:

// This one is part of external framework, or auto-generated, or dllimport, or #import, etc.. 
class A 
{ 
    protected double m_x; 
}; 

// This one is the extension class. Make sure you only add non-virtual methods here. 
// Static methods and static data members are OK as well. 
class __declspec(novtable) B: public A 
{ 
public: 
    double getSquare(){ return m_x * m_x; } 
    __declspec(property(get = getSquare)) double square; 
}; 

// Usage example 
double someFunc(A& arg) 
{ 
    B& b = static_cast<B&>(arg); // Note we've not constructed any instance of B, just casted. 
    return b.square; 
} 
+0

Oh, in realtà ho appena scoperto una tecnica simile (forse la stessa?) E ho postato una nuova [domanda] (http://stackoverflow.com/questions/14952504/is-it-possibile-per-scopiare-un-oggetto-in- -a-sottoclasse-che-non-non-definire-extra-v). Posso trattare questa tecnica è sicura per standard? – Eonil

+5

'__declspec' e' property' non fanno parte dello standard ISO C++. –

+0

Sono assolutamente sicuro che funzionasse in Visual Studio 2005 e 2008. Non so è standard, e ad essere sincero non mi interessa. "Standard C++" è solo un'altra astrazione che perde. Per assicurarti che il tuo codice C++ si sviluppi con i compilatori A, B e C, devi svilupparlo e testarlo in questo modo. – Soonts

5

Un'altra opzione, che non può essere vista come "pulita" da alcuni (sebbene sia a mio parere) ma che comunque compia la stessa cosa, sta utilizzando una classe statica. È importante ricordare che quando creiamo una funzione membro, ciò che sta realmente accadendo dietro le quinte è che la compilazione genera una funzione in cui l'oggetto (detto anche "questo") è il primo parametro. Quindi, possiamo fare la stessa cosa per estendere le funzionalità della nostra classe senza derivarne.

class Something 
{ 
public: 
    Something() 
    ~Something() 
} 

// In objective-c you may call this category Something+Utils 
class SomethingUtils 
{ 
    // You can use a pointer, or a reference here, your call. 
    static int GetSomethingElse(Something *something, int parameter); 
} 

Questo raggiungerà lo stesso intento come una categoria: si estende la funzionalità del vostro oggetto di classe, e non si deve creare una nuova classe derivata. Non sarai in grado di accedere a funzioni e variabili membro private o protette, ma non lo puoi fare comunque in ogg-c, quindi non c'è nulla di perso su quel fronte (e se stai provando ad usare uno stato membro privato o protetto, tu hai perso completamente il punto delle categorie). Non saresti in grado di usare il. e -> operatori, ma a mio parere, è un compromesso molto meglio che derivare un nuovo tipo solo per aggiungere alcuni metodi di utilità.

+0

Questo è sinteticamente brutto, ma devo ammettere che questa è l'opzione più pratica. Anche io adattare la soluzione di @leemes, ma scriverei qualche classe di utilità come questa, e la completerò. – Eonil

+5

Una leggera variazione che per certi aspetti è più stilisticamente interessante è l'uso di uno spazio dei nomi. –

+0

Aggiungerei anche Eonil, che sebbene tu possa vederlo come sintatticamente brutto (personalmente non lo faccio), è sintatticamente chiaro. È implicitamente ovvio che questa è un'estensione di classe piuttosto che un metodo di base, un problema di cui soffrono le categorie obj-c (devi seguire il metodo nel file di intestazione per rendersi conto che è una categoria). Per quanto riguarda la bruttezza, non lo discuterò, perché probabilmente non lo vedremo mai d'occhio :) –

0

ho ottenuto un quasi coerente convenzione di chiamata con an idea I talked about in a lightning talk un anno fa:

(l'intro rende meno senso senza che il commento - ora til si arriva al C++ bit).

Nota che il materiale non era destinato a essere preso sul serio - anche se alcuni, inevitabilmente, hanno ;-)