Hjälp i C++, gäller Klass variabler som reference

Permalänk

Hjälp i C++, gäller Klass variabler som reference

Tjena,

Håller på med lite programmering, är självlärd och har lite problem med pekare och referenser.

Tror jag skulle ha problem att förklara vad jag vill i ord så skriver med ett kod exempel under istället.

class abc { public: int get_i(){return i;} private: int i; }; void func(int* a) { *a = 10; } int main() { abc objekt; //Går det att skicka in get_i() funktionen som referens till func(int *a) funktionen? func(&objekt.get_i()); return 0; }

Jag skrev med min fundering som kommentar i koden.

Om det är någon som vågar sig på mitt problem med den knasiga förklaringen jag gett skulle jag vara otroligt tacksam och tack på förhand för alla svar!

Permalänk
Datavetare

Vad är det som get_i() returnerar i detta fall? Jo, det är en kopia av abc instansvariabeln i. Att ta adressen av något som returneras "by value" är nonsens då det aldrig kan resultera i en vettig sidoeffekt, så det du har skrivit kommer inte att kompilera då objekt.get_i() inte resulterar i något som rimligen kan finnas på vänster sida om "=", d.v.s. det är inte ett "lvalue".

Om du däremot ändrar definitionen av get_i() till att returnera en referens så kan func() sätta värdet av medlemsvariabel i till 10.

class abc { public: int &get_i(){return i;} private: int i; };

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

Roligt att du är nybörjare och direkt dyker på en av dem klurigaste grejerna i C++. Det går i princip att göra, men du måste deklarera en funktionspekare för varje kombination av klass och funktion. Så om två klasser A och B båda har en funktion float f(int, char*), så måste du deklarera olika funktionspekare för både A och B.

float (A::*my_A_f_ptr)(int, char *); float (B::*my_B_f_ptr)(int, char *);

Notera den underliga ::* syntaxen. Det finns betydligt mer att läsa på om om du verkligen behöver detta. Här är ett av mina favoritdokument i ämnet:

http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

Som du kan läsa där är kompilatorstödet bristfälligt för denna syntax, och min rekommendation är att lösa ditt problem på ett annat sätt. Måste du absolut, så titta på färdiga delegates (design pattern för att lösa detta)

EDIT: Jag läste för snabbt. Det du försöker göra är inte att skicka en pekare till en funktion utan till värdet som returneras av get_i(); Detta går inte då värdet som du försöker peka till inte har sparats nånstans, det vill säga det är inget l-value. Du måste antingen spara värdet innan anrop till func() eller så låter du get_i() returnera en pekare isället.

1

int i = objekt.get_i(); func(&i);

2

int* get_i() {return &i;}

Det är dock dålig kodstil att låta funktioner få direkt tillgång till klassvariabler som i exempel 2

Visa signatur

Louqe Ghost S1 MK3 | Asus ROG Strix B660-I Gaming WiFi | Intel Core i7 12700K | nVidia RTX 2070 Super FE | Corsair 64GB (2x32GB) DDR5 5600MHz CL40 Vengeance | Samsung 980 PRO M.2 NVMe SSD 2TB | Corsair SF750 750W 80+ Platinum | Noctua NH-L12 Ghost S1 edition | Kablar från pslate customs | 2 stk Dell Ultrasharp 3014 | Logitech MX Keys | Logitech MX Anywhere

Permalänk
Medlem
Skrivet av Yoshman:

Vad är det som get_i() returnerar i detta fall? Jo, det är en kopia av abc instansvariabeln i. Att ta adressen av något som returneras "by value" är nonsens då det aldrig kan resultera i en vettig sidoeffekt, så det du har skrivit kommer inte att kompilera då objekt.get_i() inte resulterar i något som rimligen kan finnas på vänster sida om "=", d.v.s. det är inte ett "lvalue".

Om du däremot ändrar definitionen av get_i() till att returnera en referens så kan func() sätta värdet av medlemsvariabel i till 10.

class abc { public: int &get_i(){return i;} private: int i; };

Det du föreslår funkar, och det ger en rolig sidoeffekt där du kan använda get_i() till att sätta klassvariabeln i.

objekt.get_i() = 2;

Det är går emot "svarta lådan" principen, så jag brukar undvika att returnera referenser. Den bästa lösningen för direkt tillgång till klassvariabler är att returnera std::share_ptr<i> eller std::weak_ptr<i>. Med dessa kan du kontrollera giltighet av pekaren när du behöver den.

Visa signatur

Louqe Ghost S1 MK3 | Asus ROG Strix B660-I Gaming WiFi | Intel Core i7 12700K | nVidia RTX 2070 Super FE | Corsair 64GB (2x32GB) DDR5 5600MHz CL40 Vengeance | Samsung 980 PRO M.2 NVMe SSD 2TB | Corsair SF750 750W 80+ Platinum | Noctua NH-L12 Ghost S1 edition | Kablar från pslate customs | 2 stk Dell Ultrasharp 3014 | Logitech MX Keys | Logitech MX Anywhere

Permalänk
Datavetare

Om man struntar i den kod du faktiskt skrivit, för den går inte riktigt att på ett rimligt sätt kombinera med "//Går det att skicka in get_i() funktionen som referens till func(int *a) funktionen?", utan läser vad du frågar efter så finns det lite tolkningsmån.

Antar att du undrar om det går att skicka en referens till en viss medlemsfunktion ihop med en specifik instans som den funktionen kan operera på. I C++11 är det enkelt via std::bind och std::function, här är ett exempel på hur func() skulle kunna se ut för att kunna anropa vilken funktion (inklusive medlemsfunktion) som helst.

#include <iostream> #include <functional> class abc { public: int get_i() { return i; } void set_i(int new_i) { i = new_i; } private: int i; }; void func(std::function<void(int)> set_something) { set_something(10); } int func2(std::function<int(void)> get_something) { return get_something(); } int main() { abc objekt; // Use std::bind to create an unary function that call // abc::set_i on the instance 'objekt' func(std::bind(&abc::set_i, &objekt, std::placeholders::_1)); // Use std::bind to create a constant function that call // abc::get_i on the instance 'objekt' std::cout << func2(std::bind(&abc::get_i, &objekt)) << std::endl; }

Det är möjligt att göra detta även i pre-C++11 via wrapper template-klasser som implementerar ret_type operator()(args...) för varje funktionssignatur som behövs. Men varför inte använda C++11? I C++11 kan du även använda closures för att lösa ditt problem så finns flera sätt.

Skrivet av sunefred:

Det du föreslår funkar, och det ger en rolig sidoeffekt där du kan använda get_i() till att sätta klassvariabeln i.

objekt.get_i() = 2;

Det är går emot "svarta lådan" principen, så jag brukar undvika att returnera referenser. Den bästa lösningen för direkt tillgång till klassvariabler är att returnera std::share_ptr<i> eller std::weak_ptr<i>. Med dessa kan du kontrollera giltighet av pekaren när du behöver den.

Försökte bara svara på TS fråga utan att lägga värdering i huruvida det är bad-practice eller ej. Att använda smarta pekare är ju ingen större förbättring jämfört med att ge en referens, om man inte har sin medlemsvariabel som just en std::shared_ptr<int> så finns ju än fler sätt att skjuta sig i foten om man blandar i detta jämfört med att ge en direkt referens.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer