Skambinimas klasės C ++ metodais, naudojant funkcijų rodyklę

Kaip gauti funkcijų rodyklę klasės funkcijai ir tada paskambinti šią nario funkciją konkrečiu objektu? Id norite rašyti:

 class Dog : Animal { Dog (); void bark (); } … Dog* pDog = new Dog (); BarkFunction pBark =  (*pBark) (pDog); … 

Be to, jei įmanoma, ID norėtų paskambinti konstruktoriui naudojant žymiklį:

 NewAnimalFunction pNew =  Animal* pAnimal = (*pNew)(); 

Ar tai įmanoma, ir jei taip, koks yra pageidaujamas būdas tai padaryti?

91
28 сент. Tony Pony yra nustatytas rugsėjo 28 d. 2009-09-28 11:30 '09 11:30 val. 2009-09-28 11:30
@ 10 atsakymų

Daugiau apie tai skaitykite:

// 1 nustatyti funkcijų rodyklę ir inicijuoti reikšmę NULL

 int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL; 

// C + +

 class TMyClass { public: int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;}; int DoMore(float a, char b, char c) const { cout << "TMyClass::DoMore" << endl; return a-b+c; };  }; pt2ConstMember =  // note: <pt2Member> may also legally point to  // Calling Function using Function Pointer (*this.*pt2ConstMember)(12, 'a', 'b'); 
97
28 сент. Satbir paskelbė rugsėjo 28 dieną 2009-09-28 13:04 '09, 13:04 2009-09-28 13:04

Kaip gauti rodyklę į klasės funkciją ir paskambinti šią nario funkciją konkrečiu objektu?

Lengviausias būdas pradėti yra typedef . Nario funkcijai pridėti klasės pavadinimą prie tipo deklaracijos:

 typedef void(Dog::*BarkFunction)(void); 

Tada, norėdami paskambinti šiuo metodu, naudojate operatorių ->* :

 (pDog->*pBark)(); 

Taip pat, jei įmanoma, Id norėtų nurodyti konstruktorių su rodykle. Ar tai įmanoma, ir jei taip, koks yra pageidaujamas būdas tai padaryti?

Nemanau, kad galite dirbti su dizaineriais, tokiais kaip gydytojai ir dorai. Įprastas būdas pasiekti tokį dalyką yra gamyklos metodas, kuris iš esmės yra statinė funkcija, kuri jums pareikalauja. Žr. Toliau pateiktą kodą.

border=0

Pakeitiau jūsų kodą, kad tai padarytum iš esmės. Yra keletas įspėjimų.

 #include <iostream> class Animal { public: typedef Animal*(*NewAnimalFunction)(void); virtual void makeNoise() { std::cout << "M00f!" << std::endl; } }; class Dog : public Animal { public: typedef void(Dog::*BarkFunction)(void); typedef Dog*(*NewDogFunction)(void); Dog () {} static Dog* newDog() { return new Dog; } virtual void makeNoise () { std::cout << "Woof!" << std::endl; } }; int main(int argc, char* argv[]) { // Call member function via method pointer Dog* pDog = new Dog (); Dog::BarkFunction pBark =  (pDog->*pBark)(); // Construct instance via factory method Dog::NewDogFunction pNew =  Animal* pAnimal = (*pNew)(); pAnimal->makeNoise(); return 0; } 

Dabar, nors dėl polimorfizmo magijos paprastai galite naudoti Dog* vietoj Animal* , funkcijų rodyklės tipas neatitinka klasės hierarchijos paieškos taisyklių. Taigi, gyvūnų metodo rodyklė yra nesuderinama su šuns metodo rodykle, kitaip tariant, jūs negalite priskirti Dog* (*)() kintamąjį Animal* (*)() .

Statinis newDog metodas yra paprastas gamyklos pavyzdys, kuris tiesiog sukuria ir grąžina naujus atvejus. Būdama statine funkcija, ji turi reguliarų typedef (be klasių klasifikatoriaus).

Atsakydamas į pirmiau minėtą klausimą, norėčiau sužinoti, ar yra geresnis būdas pasiekti tai, ko jums reikia. Yra keletas konkrečių scenarijų, kuriuose atliksite tokius dalykus, tačiau galite rasti kitų šablonų, kurie geriausiai tinka jūsų problemai. Jei išsamiau aprašysite, ką bandote pasiekti, avilių protas gali būti dar naudingesnis!

Atsižvelgiant į tai, kas išdėstyta pirmiau, jūs tikrai rasite labai naudingą „ Boost bind“ biblioteką ir kitus susijusius modulius.

50
28 сент. atsakymas pateikiamas gavinb 28 sep . 2009-09-28 13:20 '09, 13:20 2009-09-28 13:20

Nemanau, kad kažkas čia paaiškino, kad viena problema yra ta, kad jums reikia „ nario rodyklių “, o ne įprastų funkcijų rodyklės.

Funkcijų žymeklio elementai yra ne tik funkcijų rodikliai. Kalbant apie įgyvendinimą, kompiliatorius negali naudoti paprasto funkcijos adreso, nes paprastai nežinote adreso, kurį norite skambinti, kol nežinote, kuris objektas priklauso dereferencei (pagalvokite apie virtualias funkcijas). Žinoma, jūs taip pat turite žinoti objektą, kad pateiktumėte this numanomą parametrą.

Sakydamas, kad jums jų reikia, dabar pasakysiu, kad jums tikrai reikia jų išvengti. Rimtai, narių indeksai yra skausmas. Labiau protinga ieškoti objektų orientuotų dizaino modelių, kurie pasiektų tą patį tikslą, arba naudokite funkciją „ boost::function arba kažką panašaus, kaip minėta pirmiau, jei norite padaryti šį pasirinkimą.

Jei nurodote šį rodiklį į esamą kodą, jums tikrai reikia paprasto rodyklės į funkciją, turite rašyti funkciją kaip statinį klasės narį. Statinė nario funkcija tai nesupranta, taigi jums reikia perduoti objektą aiškiame parametre. Kartais šiose eilutėse buvo neįprastas darbas su senuoju C kodu, kuriam reikia funkcijų rodiklių

 class myclass { public: virtual void myrealmethod () = 0; static void myfunction (myclass *p); } void myclass::myfunction (myclass *p) { p->myrealmethod (); } 

Kadangi myfunction funkcija yra normali funkcija (problemos, susijusios su apimtimi), funkcijų rodyklę galima rasti įprastu C režimu.

EDIT - šis metodas vadinamas „klasės metodu“ arba „statiniu nario funkcija“. Pagrindinis skirtumas tarp funkcijos, kuri nėra nario funkcija, yra tai, kad, jei nurodysite jį iš klasės ribų, turite nurodyti taikymo sritį naudodamiesi :: taikymo sritis. Pavyzdžiui, norėdami gauti rodyklę į funkciją, naudokite > ir, jei norite jį pavadinti, naudokite myclass::myfunction (arg); ,

Tokie dalykai yra gana paplitę naudojant senas Win32 API, kurios iš pradžių buvo skirtos C, o ne C ++. Žinoma, šiuo atveju parametras dažniausiai yra LPARAM arba panašus, o ne rodyklė, o tam tikras liejimas yra būtinas.

27
28 сент. atsakymą pateikė Steve314 28 sep . 2009-09-28 17:54 '09, 17:54, 2009-09-28 17:54
 typedef void (Dog::*memfun)(); memfun doSomething =  .... (pDog->*doSomething)(); // if pDog is a pointer // (pDog.*doSomething)(); // if pDog is a reference 
17
28 сент. AraK atsakymas į 28 sep . 2009-09-28 11:33 '09 11:33 am 2009-09-28 11:33

Aš atėjau čia ir sužinojau, kaip sukurti metodo rodyklę (o ne metodo rodyklę), tačiau nė vienas čia pateiktas atsakymas nesuteikia sprendimo. Taigi aš galvojau apie tai ir suradau gerą sprendimą, kuris, manau, yra verta pasidalinti:

 template <class T> struct MethodHelper; template <class C, class Ret, class... Args> struct MethodHelper<Ret(C::*)(Args...)> { using T = Ret (C::*)(Args...); template <T m> static Ret call(C* object, Args... args) { return (object->*m)(args...); } }; #define METHOD_FP(m) MethodHelper<decltype(m)>::call<m> 

Taigi, pvz., Dabar vykdote:

 Dog dog; using BarkFunction = void (*)(Dog*); BarkFunction bark = METHOD_FP( (*bark)( // or simply bark(> 
7
18 июня '16 в 13:15 2016-06-18 13:15 atsakymas pateikiamas blakstienų birželio 18 d., 16 val. 15.15 val. 2016-06-18 13:15

Minimalus priimtinas pavyzdys

 #include <cassert> class C { public: int i; C(int i) : i(i) {} int m(int j) { return this->i + j; } }; int main() { // Get a method pointer. int (C::*p)(int) =  // Create a test object. C c(1); C *cp =  // Operator .* assert((c.*p)(2) == 3); // Operator ->* assert((cp->*p)(2) == 3); } 

Jūs negalite keisti skliausteliuose nurodytos tvarkos arba jų negalima praleisti. Šie veiksmai neveikia:

 c.*p(2) c.*(p)(2) 

C ++ 11 standartas

.* ir ->* yra vieninteliai operatoriai, kurie šiuo tikslu yra įdiegti C ++ ir nėra C.

C ++ 11 standartinis projektas N3337 :

  • 2.13 „Operatoriai ir skyrybos ženklai“ turi visų operatorių, kuriuose yra .* Ir ->* .
  • 5.5 „nario operatoriai“ paaiškina, ką jie daro
7
01 июля '15 в 12:06 2015-07-01 12:06 atsakymą pateikė Ciro Santilli 改造 改造 中心 六四 事件 法轮功 liepos 1 d. 15:06 2015-07-01 12:06

Priežastis, kodėl negalite naudoti funkcijų rodyklių skambinti nario funkcijoms, yra ta, kad normalios funkcijos rodyklės paprastai yra tik funkcijos atminties adresas.

Jei norite paskambinti nario funkcijai, turite žinoti du dalykus:

  • Kokia nario funkcija skambinti
  • Kuris pavyzdys turėtų būti naudojamas (kurio nario funkcija)

Įprastinės funkcijos rodyklės negali saugoti abiejų. Rodyklės į C + + funkcijas yra naudojamos išsaugoti a), todėl jums reikia aiškiai nurodyti egzempliorių, kai skambinate funkcijų nario rodykle.

6
28 сент. atsakymas pateikiamas hrnt rugsėjo 28 d 2009-09-28 11:55 '09 at 11:55 2009-09-28 11:55

Funkcijos rodyklė klasės nariui yra problema, kuri yra tikrai tinkama naudoti:. Mažas pavyzdys:

 #include <boost/function.hpp> #include <iostream> class Dog { public: Dog (int i) : tmp(i) {} void bark () { std::cout << "woof: " << tmp << std::endl; } private: int tmp; }; int main() { Dog* pDog1 = new Dog (1); Dog* pDog2 = new Dog (2); //BarkFunction pBark =  boost::function<void (Dog*)> f1 =  f1(pDog1); f1(pDog2); } 
6
28 сент. Atsakymas, kurį pateikė Benjamin Sep 28 2009-09-28 16:18 '09, 16:18, 2009-09-28 16:18

Norėdami sukurti naują objektą, galite naudoti naują vietą, kaip nurodyta aukščiau, arba įdiegti savo klono () metodą, kuris sukuria objekto kopiją. Tada šį klonavimo metodą galite paskambinti naudodami nario funkcijų rodyklę, kaip aprašyta aukščiau, kad sukurtumėte naujus objekto atvejus. Klono pranašumas yra tas, kad kartais galite dirbti su rodykle į pagrindinę klasę, kur nežinote objekto tipo. Tokiu atveju klono () metodas gali būti lengviau naudojamas. Be to, klonas () leis jums kopijuoti objekto būseną, jei to norite.

2
28 сент. Corwin Joy atsakymas rugsėjo 28 d 2009-09-28 12:34 '09 12:34 2009-09-28 12:34

Aš vis dar nesuprantu, kodėl, jei norite skambinti objekto nario funkcijai ir tada tiesiog perkelti žymiklį į objektą? Jei žmonės skundžiasi tuo, kad tai leidžia geriau apsaupti klasę, kodėl gi ne sukurti sąsajos klasę, kuri paveldėtų iš visos klasės?

0
28 сент. atsakymas pateikiamas Čadas 28 rugsėjis 2009-09-28 11:39 '09 11:39 2009-09-28 11:39

Kiti klausimai apie „ žymeklius arba „ Klauskite“