Domanda

Molti libri ed esercitazioni in C ++ spiegano come farlo, ma non ne ho visto uno che dia una ragione convincente per scegliere di farlo.

Comprendo molto bene perché i puntatori a funzione fossero necessari in C (ad esempio, quando si utilizzano alcune funzionalità POSIX). Tuttavia, AFAIK non puoi inviare loro una funzione membro a causa di " questo " parametro. Ma se stai già usando classi e oggetti, perché non usare una soluzione orientata agli oggetti come i funzione?

Sarebbero apprezzati esempi reali di dove dovevi usare tali puntatori a funzione.

Aggiornamento: apprezzo le risposte di tutti. Devo dire, tuttavia, che nessuno di questi esempi mi convince davvero che si tratta di un meccanismo valido dal punto di vista del puro OO ...

È stato utile?

Soluzione

I Functor non sono a priori orientati agli oggetti (in C ++, il termine & # 8220; functor & # 8221; di solito significa una struttura che definisce un operator () con argomenti arbitrari e valore di ritorno che può essere utilizzato come sostituto drop-in sintattico di funzioni reali o puntatori di funzioni). Tuttavia, il loro problema orientato agli oggetti ha molti problemi, in primo luogo l'usabilità. È solo un sacco di complicato codice boilerplate. Per un framework di segnalazione decente come nella maggior parte dei framework di dialogo, diventa necessario un sacco di pasticcio di eredità.

I puntatori a funzioni associate all'istanza sarebbero molto utili qui (.NET lo dimostra ampiamente con i delegati).

Tuttavia, i puntatori a funzioni membro C ++ soddisfano ancora un'altra necessità. Immagina, ad esempio, di avere molti valori in un elenco di cui desideri eseguire un metodo, ad esempio print () . Un puntatore a YourType :: size aiuta qui perché ti permette di scrivere tale codice:

std::for_each(lst.begin(), lst.end(), std::mem_fun(&YourType::print))

Altri suggerimenti

In passato, i puntatori alle funzioni membro erano utili in scenari come questo:

class Image {
    // avoid duplicating the loop code
    void each(void(Image::* callback)(Point)) {
        for(int x = 0; x < w; x++)
            for(int y = 0; y < h; y++)
                callback(Point(x, y));
    }

    void applyGreyscale() { each(&Image::greyscalePixel); }
    void greyscalePixel(Point p) {
        Color c = pixels[p];
        pixels[p] = Color::fromHsv(0, 0, (c.r() + c.g() + c.b()) / 3);
    }

    void applyInvert() { each(&Image::invertPixel); }
    void invertPixel(Point p) {
        Color c = pixels[p];
        pixels[p] = Color::fromRgb(255 - c.r(), 255 - r.g(), 255 - r.b());
    }
};

L'ho visto usato in un'app di pittura commerciale. (interessante, è uno dei pochi problemi di C ++ meglio risolti con il preprocessore).

Oggi, tuttavia, l'unico uso per i puntatori alle funzioni membro è all'interno dell'implementazione di boost :: bind .

Ecco uno scenario tipico che abbiamo qui. Abbiamo un framework di notifica, in cui una classe può registrarsi a più notifiche diverse. Quando ci registriamo a una notifica, passiamo il puntatore alla funzione membro. Questo è in realtà molto simile agli eventi C #.

class MyClass
{
    MyClass()
    {
        NotificationMgr::Register( FunctionPtr( this, OnNotification ) );
    }
    ~MyClass()
    {
        NotificationMgr::UnRegister( FunctionPtr( this, OnNotification ) );
    }

    void OnNotification( ... )
    {
        // handle notification
    }
};

Ho un po 'di codice su cui sto lavorando proprio ora dove li ho usati per implementare una macchina a stati. Le funzioni membro senza referenze implementano gli stati, ma poiché sono tutti nella classe, riescono a condividere una quantità di dati certian che è globale per l'intera macchina a stati. Sarebbe stato difficile da realizzare con normali puntatori a funzioni (non membri).

Sono ancora indeciso se questo è un buon modo per implementare una macchina a stati.

È come usare lambdas. Puoi sempre passare tutte le variabili locali necessarie a una semplice funzione, ma a volte devi passare più di una di esse.

Quindi l'uso delle funzioni membro ti salverà dal passaggio di tutti i campi membri necessari a un funzione. È tutto.

Hai chiesto in modo specifico le funzioni membro, ma ci sono anche altri usi per i puntatori funzione. Il motivo più comune per cui ho bisogno di usare i puntatori a funzione in C ++ è quando voglio caricare un runtime di DLL usando LoadLibrary (). Questo è in Windows, ovviamente. Nelle applicazioni che utilizzano plug-in sotto forma di DLL opzionali, il collegamento dinamico non può essere utilizzato all'avvio dell'applicazione poiché la DLL spesso non sarà presente e l'utilizzo di delayload è una seccatura.

Dopo aver caricato la libreria, è necessario ottenere un puntatore alle funzioni che si desidera utilizzare.

Ho usato i puntatori di funzione membro che analizzano un file. A seconda delle stringhe specifiche trovate nel file, lo stesso valore è stato trovato in una mappa e la funzione associata chiamata. Si trattava invece di una grande istruzione if..else if..else che confrontava le stringhe.

L'uso singolo più importante dei puntatori membro è la creazione di funzioni. La buona notizia è che non hai nemmeno bisogno di usarlo direttamente, poiché è già risolto nelle librerie come boost :: bind, ma devi passare i puntatori a quelle librerie.

class Processor
{
public:
   void operation( int value );
   void another_operation( int value );
};
int main()
{
   Processor tc;
   boost::thread thr1( boost::bind( &Processor::operation, &tc, 100 ) );
   boost::thread thr2( boost::bind( &Processor::another_operation, &tc, 5 ) );
   thr1.join();
   thr2.join();
}

Puoi vedere la semplicità della creazione di un thread che esegue una determinata operazione su una determinata istanza di una classe.

Il semplice approccio fatto a mano al problema sopra sarebbe sulla linea di creare tu stesso un funzione:

class functor1
{
public:
    functor1( Processor& o, int v ) : o_(o), v_(v) {}
    void operator()() {
        o_.operation( v_ ); // [1]
    }
private:
    Processor& o_;
    int v_;
};

e creane uno diverso per ogni funzione membro che desideri chiamare. Si noti che il funzione è esattamente lo stesso per operazione e per operazione_altro , ma la chiamata in [1] dovrebbe essere replicata in entrambi i funzioni. Usando un puntatore alla funzione membro puoi scrivere un semplice funzione:

class functor
{
public:
   functor( void (*Processor::member)(int), Processor& p, int value )
      : member_( member ), processor_(p), value_( value ) {}

   void operator()() {
      p.*member(value_);
   }
private:
   void (*Processor::member_)(int);
   Processor& processor_;
   int value;
};

e usalo:

int main() {
   Processor p;
   boost::thread thr1( functor( &Processor::operation, p, 100 ) );
   boost::thread thr2( functor( &Processor::another_operation, p, 5 ) );
   thr1.join();
   thr2.join();
}

Quindi, non è nemmeno necessario definire quel funzione come boost :: bind lo fa per te. Il prossimo standard avrà una sua versione di bind lungo le linee di implementazione di boost.

Un puntatore a una funzione membro è indipendente dall'oggetto. È necessario se si desidera fare riferimento a una funzione in base al valore in fase di esecuzione (o come parametro del modello). Entra in proprio quando non hai in mente un singolo oggetto su cui chiamarlo.

Quindi, se conosci la funzione, ma non conosci l'oggetto E desideri trasmettere questa conoscenza per valore, allora la funzione punto-membro è l'unica prescritta. L'esempio di Iraimbilanja lo illustra bene. Può aiutarti a vedere il mio esempio di utilizzo di una variabile membro . Il principio è lo stesso.

Ho usato un puntatore a una funzione membro in uno scenario in cui dovevo fornire un puntatore a una richiamata con un elenco di parametri predefinito (quindi non sono riuscito a passare parametri arbitrari) a qualche oggetto API di terze parti.

Non ho potuto implementare il callback nello spazio dei nomi globale perché doveva gestire gli eventi in arrivo in base allo stato dell'oggetto che utilizzava l'API di terze parti che aveva attivato il callback.

Quindi volevo che l'implementazione del callback facesse parte della classe che faceva uso dell'oggetto di terze parti. Quello che ho fatto è, ho dichiarato una funzione membro pubblica e statica nella classe in cui volevo implementare il callback e ho passato un puntatore ad esso sull'oggetto API (la parola chiave static risparmiando il questo problema con il puntatore).

Il puntatore this del mio oggetto verrebbe quindi passato come parte del Refcon per il callback (che fortunatamente conteneva un void * generico). L'implementazione del manichino ha quindi utilizzato il puntatore passato per richiamare l'implementazione effettiva e privata del callback contenuto nella classe =).

Sembrava qualcosa del genere:

public:
    void SomeClass::DummyCallback( void* pRefCon ) [ static ]
    {
        reinterpret_cast<SomeClassT*>(pRefCon)->Callback();
    }
private:
    void class SomeClass::Callback() [ static ]
    {
        // some code ...
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top