Permalänk
Medlem

Struct till Class, C++

Har slitit lite med ett problem jag har nu.
I min main.cpp så skapar jag en struct med lite data i. Denna struct vill jag sedan skicka som ett argument när jag skapar ett objekt. Sedan när objektet har fått den så ska objektet kunna modifiera datan i structen.

Min första approach som jag försökt med är att skicka structen som en pointer för att sedan lagra pointern i objektet för att vidare kunna modifiera datan i structen via objektets metoder, men verkar som att jag går bet på syntaxen här lite grann.
Visar nedan relevanta delar av de 3 filerna så kanske någon kan pusha i rätt riktning:

main.cpp

int main(int argc, char** argv) { struct StatusValues { std::string m_latestGModalMode = "N/A"; std::string m_latestX = "N/A"; std::string m_latestY = "N/A"; std::string m_latestZ = "N/A"; std::string m_latestI = "N/A"; std::string m_latestJ = "N/A"; std::string m_latestK = "N/A"; }; StatusValues currentValues; while (std::getline(input_file, completeLine)) { LineAnalyze theLine(completeLine, &currentValues); //Här skapas objektet, vill skicka med currentValues ....

LineAnalyze.h

#pragma once #include <string> struct StatusValues; class LineAnalyze { public: LineAnalyze(const std::string&, StatusValues*); private: StatusValues* m_struct; };

LineAnalyze.cpp

#include "LineAnalyze.h" #include <iostream> #include <vector> LineAnalyze::LineAnalyze(const std::string& line, StatusValues* s) { //I detta skede skulle jag nu vilja kunna printa ett värde från structen typ: std::cout << s.m_latestX << std::endl; //Och senare i objektet kunna modifiera värdena i structen i andra metoder, typ: m_struct = s; m_struct.m_latestX = "Nytt värde som nu kan läsas i main.cpp"; }

Om jag är ute och cyklar med approachen så blir jag gärna tillrättavisad, om jag är rätt ute kanske jag kan få lite syntax-hjälp^^

Tack på förhand

Permalänk
Hedersmedlem

Hej!

Du ska inte deklarera structen på sättet du gör, genom att bara ha den i main-funktionen. Istället ska du deklarara structen i en header-fil som du inkluderar i alla filer som behöver veta hur den structen är uppbyggd.

Du vill förmodligen inte heller skapa structen på stacken som du gör, du vill skapa en ny instans med "new". Annars kommer du, så fort du returnerat från funktionen där structen ligger på stacken att råka ut för att pekaren pekar till minne som nu används till något annat. (Detta är ju dock helt okej så länge som objektet aldrig lever längre än funktionen där du definierat structen.)

(Tips, struct och class är nästan exakt samma sak i C++, enda viktiga skillnaden är att struct har alla members som public som standard. )

Sista grejen, om du har en pekare till en struct (eller class för den delen), typ:

minstruct *s;
// s sätts till något bra någonstans i koden

Så ska du använda -> när du läser ut värden ur pekaren.

cout << s->myString << endl;

a->b är exakt samma sak som (*a).b, bara en smidigare syntax.

Permalänk
Medlem

Det kan även vara enklare att skicka och lagra struct-instansen som en referens istället för en pekare, syntaxen blir lite enklare och den kommer då alltid att peka på ett objekt till skillnad mot en pekare som kan vara nullptr (vilket man förstås ibland vill, men verkar inte som att det är önskvärt här).

Du måste i så fall initialisera referensen i en initialiserings-lista istället för att tilldela den i konstruktorn, eftersom den alltid måste peka på något. D.v.s.:

LineAnalyze::LineAnalyze(const std::string& line, StatusValues& s) : m_struct(s) { ... }

Men att använda en initialiserings-lista bör du ju alltid göra ändå. Sen har referenser sina egna fällor som kan ställa till det, på samma sätt som pekare har. Men det är svårt att säga vad den bästa lösningen är baserat på den begränsade informationen vi har.

Permalänk
Medlem
Skrivet av pv2b:

Hej!

Du ska inte deklarera structen på sättet du gör, genom att bara ha den i main-funktionen. Istället ska du deklarara structen i en header-fil som du inkluderar i alla filer som behöver veta hur den structen är uppbyggd.

Du vill förmodligen inte heller skapa structen på stacken som du gör, du vill skapa en ny instans med "new". Annars kommer du, så fort du returnerat från funktionen där structen ligger på stacken att råka ut för att pekaren pekar till minne som nu används till något annat. (Detta är ju dock helt okej så länge som objektet aldrig lever längre än funktionen där du definierat structen.)

(Tips, struct och class är nästan exakt samma sak i C++, enda viktiga skillnaden är att struct har alla members som public som standard. )

Sista grejen, om du har en pekare till en struct (eller class för den delen), typ:

minstruct *s;
// s sätts till något bra någonstans i koden

Så ska du använda -> när du läser ut värden ur pekaren.

cout << s->myString << endl;

a->b är exakt samma sak som (*a).b, bara en smidigare syntax.

Grymt nice, har fått det att fungera nu tror jag.
Har lagt structen i en egen header som inkluderas där den behövs.

Skapar sedan instansen enligt ditt förslag(som jag tolkade det):

StatusValues* currentValue = new StatusValues;

I min klass ser sedan konstruktorn ut enligt:

LineAnalyze::LineAnalyze(const std::string& line, StatusValues* s) { m_currentStatus = s; //Detta är en private i klassen som lagrar pointern för vidare användning senare. //Lite tester bara som visar att det fungerar std::cout << m_currentStatus->m_latestGModalMode << std::endl; m_currentStatus->m_latestGModalMode = "NYTT!"; std::cout << m_currentStatus->m_latestGModalMode << std::endl; }

Misstänker att största "felet" jag gjorde innan var att inte ha struct i en egen header då jag aldrig lyckades skicka med en pointer som argument.

Får tacka och buga!

Permalänk
Hedersmedlem
Skrivet av bardbard:

Grymt nice, har fått det att fungera nu tror jag.
Har lagt structen i en egen header som inkluderas där den behövs.

Skapar sedan instansen enligt ditt förslag(som jag tolkade det):

StatusValues* currentValue = new StatusValues;

I min klass ser sedan konstruktorn ut enligt:

LineAnalyze::LineAnalyze(const std::string& line, StatusValues* s) { m_currentStatus = s; //Detta är en private i klassen som lagrar pointern för vidare användning senare. //Lite tester bara som visar att det fungerar std::cout << m_currentStatus->m_latestGModalMode << std::endl; m_currentStatus->m_latestGModalMode = "NYTT!"; std::cout << m_currentStatus->m_latestGModalMode << std::endl; }

Misstänker att största "felet" jag gjorde innan var att inte ha struct i en egen header då jag aldrig lyckades skicka med en pointer som argument.

Får tacka och buga!

Glöm inte att lära dig "delete" nu också. Om du bara skapar objekt med new, och aldrig tar bort dem, så ligger de där och skvalpar i din dators minne för evigt. (Eller i alla fall till att processen avslutas.)

Permalänk
Medlem
Skrivet av pv2b:

Glöm inte att lära dig "delete" nu också. Om du bara skapar objekt med new, och aldrig tar bort dem, så ligger de där och skvalpar i din dators minne för evigt. (Eller i alla fall till att processen avslutas.)

Delete har jag lyckats snappa upp under någon tutorial att den är bra att köra om man använder new ^^

Har dock inte greppat detta med stack och heap riktigt men misstänker att det spelar mindre roll för mig som hobbyknackare.

Permalänk
Hedersmedlem
Skrivet av bardbard:

Delete har jag lyckats snappa upp under någon tutorial att den är bra att köra om man använder new ^^

Har dock inte greppat detta med stack och heap riktigt men misstänker att det spelar mindre roll för mig som hobbyknackare.

Det är rätt viktigt att förstå det så länge du jobbar med språk som C eller C++ som förutsätter att du hanterar ditt eget minne och pekare.

Finns många sätt att lära sig detta på, men om du är intresserad av att lära dig mer om hur program fungerar "under the hood" och är intresserad av datorsäkerhet så kan jag tipsa om LiveOverflows Youtube-kanal och specifikt spellistan Binary Exploitation / Memory Corruption by LiveOverflow.

Specifikt det här kapitlet om hur "heap" fungerar kan hjälpa dig förstå hur den strukturen funkar. När du ser "malloc" och "free", tänk "new" och "delete", det är i princip samma sak.

Permalänk
Medlem

@bardbard: Egentligen bör du inte använda new/delete heller, utan smarta pekare (oftast std::unique_ptr, i sällsynta fall std::shared_ptr när man har flera ägare). I modern C++ är det väldigt ovanligt att man använder new/delete och ägande pekare, d.v.s. pekare som "äger" minne som man måste frigöra manuellt.

I ditt fall så blir det då alltså:

#include <memory> // för std::unique_ptr std::unique_ptr<StatusValues> currentValue = std::make_unique<StatusValues>(); // Eller vanligtvis den kortare formen: auto currentValue = std::make_unique<StatusValue>(); LineAnalyze theLine(completeLine, currentValues.get()); // Ingen delete, unique_ptr frigör automatiskt minnet när variabeln faller ur scope.

Notera här att det är helt ok för LineAnalyze att innehålla en vanligt pekare, eftersom det då är en observerande pekare och inte en ägande pekare (d.v.s. den pekar på minne som någon annan allokerat och är ansvarig för). Vissa missförstår ibland och tror att man bör undvika alla pekare.

Permalänk
Medlem

unique_ptr och shared_ptr är najs men jag tror som ny kan man vänta med det.
jag hade rekomenderat att hålla det så simpelt som möjligt och inte använda refernser och pekare. ta saker in "by value" (inga & eller *) och så kan du ändra o pilla bäst du vill.
sen i main kan du hämta ut det modifierade från din struct ifall för att komma åt den ändrade datan.

det finns lite för många sätt att göra samma sak i c++ kan man säga

Permalänk
Hedersmedlem
Skrivet av Frappee:

unique_ptr och shared_ptr är najs men jag tror som ny kan man vänta med det.
jag hade rekomenderat att hålla det så simpelt som möjligt och inte använda refernser och pekare. ta saker in "by value" (inga & eller *) och så kan du ändra o pilla bäst du vill.
sen i main kan du hämta ut det modifierade från din struct ifall för att komma åt den ändrade datan.

det finns lite för många sätt att göra samma sak i c++ kan man säga

Jo, lite så, frågan är om C eller C++ är rätt språk om man bara ska "hemmaknacka" lite.

Men nu ska vi ju helst inte starta ett programmeringsspråkskrig i den här tråden.

Permalänk
Medlem

C++ som i detta fallet är sjukt onödigt då jag skulle kunna kört detta i python med samma resultat men utvecklingstiden skulle väl vara halverad.

Har mer fått ett sug att ”känna” på c++ och detta var ett lämpligt litet projekt, inbillar mig att det kan ge något för mitt arduino-intresse också.

Permalänk
Medlem

Jag är helt för modern C++, men övertygad om att unique_ptr bara komplicerar saker i onödan. New och delete är inte svårt. Det är heller inte dåligt, och skulle heller inte säga att det blivit ovanligt.

Permalänk
Medlem
Skrivet av herkkä:

Jag är helt för modern C++, men övertygad om att unique_ptr bara komplicerar saker i onödan. New och delete är inte svårt. Det är heller inte dåligt, och skulle heller inte säga att det blivit ovanligt.

Får väl hålla med om att new och delete inte är svåra som koncept. Det roliga börjar när man inte har klara ägarskapsförhållanden. Exempelvis när du vill göra en dynamisk allokering i ett funktionsanrop och returnera pekaren till det objektet ett par steg i anropskedjan. Det gäller att man kommer ihåg att göra delete på rätt ställe om man inte vill läcka minne. Extra roligt blir det om du någonstans i samma anropskedja dessutom råkar ut för ett exception. Det finns tillfällen då unique_ptr har sina poänger...

Permalänk
Medlem
Skrivet av Ingetledigtnamn:

Får väl hålla med om att new och delete inte är svåra som koncept. Det roliga börjar när man inte har klara ägarskapsförhållanden. Exempelvis när du vill göra en dynamisk allokering i ett funktionsanrop och returnera pekaren till det objektet ett par steg i anropskedjan. Det gäller att man kommer ihåg att göra delete på rätt ställe om man inte vill läcka minne. Extra roligt blir det om du någonstans i samma anropskedja dessutom råkar ut för ett exception. Det finns tillfällen då unique_ptr har sina poänger...

Säkerligen, men det är ju edgecases, definitivt ingen anledning att köra unique_ptr överallt och hävda att man nästan aldrig bör använda new/delete

Permalänk
Datavetare
Skrivet av herkkä:

Säkerligen, men det är ju edgecases, definitivt ingen anledning att köra unique_ptr överallt och hävda att man nästan aldrig bör använda new/delete

Just p.g.a. fall som @Ingetledigtnamn nämner ovan och andra udda fall som gör att det nästan alltid blir rätt om man försöker sig på manuell minneshantering är exakt varför man konsekvent aldrig ska använda new/delete i "modern" C++.

Huvudvalet ska i princip alltid vara att jobba "by-value" så långt som möjligt i C++. Dagens kompilatorer kan ofta göra heltcoola optimeringar i de lägena som eliminerar de flesta "onödiga" kopieringar och liknande. Vidare minskar risken att skjuta sig i foten när man jobbar med högre ordningens funktioner i "modern C++" rejält om man kör "by value", går inte det ska man absolut konsekvent använda std::unique_ptr<> eller std::shared_ptr<>. std::shared_ptr<> är kanske enklare för nybörjare då det blir mer likt hur man jobbar i språk med GC (man får dock akta sig för cirkel-referenser).

Vidare är just minneskopiering något moderna CPUer är exceptionellt snabba på så länge det handlar om "rimliga" storlekar. Mellan tummen och pekfingret kan man säga att alla minneskopieringar upp till en "page" (4-16 kB) är i praktiken "gratis", eller mer konkret: om sådana kopieringar kan förenkla koden är nettoeffekten ofta att prestanda går upp. Så även det är en orsak att föredra "by-value".

Såg en kommentar i en C++ vs Rust diskussion som jag först trodde var ett skämt, men visade sig i senare kommentarer att personen var helt alvarlig. Hen menade på att Rust "borrow-checker" var helt onödig för man kan ju göra samma sak "i huvudet" när man jobbar i C++ och det blir "i princip" alltid rätt... Grejen är att när det blir fel har man saker som use-after-free, double-free och, än värre, data-race. D.v.s. bland de absolut värsta buggarna att försöka hitta i live-system.

Permalänk
Medlem
Skrivet av herkkä:

Säkerligen, men det är ju edgecases, definitivt ingen anledning att köra unique_ptr överallt och hävda att man nästan aldrig bör använda new/delete

fast Jo, ska man allokera minne på heapen så i första hand bör man sträcka sig för unique ptr om man inte har god anledning till något annat.

Permalänk
Medlem
Skrivet av Yoshman:

Just p.g.a. fall som @Ingetledigtnamn nämner ovan och andra udda fall som gör att det nästan alltid blir rätt om man försöker sig på manuell minneshantering är exakt varför man konsekvent aldrig ska använda new/delete i "modern" C++.

Huvudvalet ska i princip alltid vara att jobba "by-value" så långt som möjligt i C++. Dagens kompilatorer kan ofta göra heltcoola optimeringar i de lägena som eliminerar de flesta "onödiga" kopieringar och liknande. Vidare minskar risken att skjuta sig i foten när man jobbar med högre ordningens funktioner i "modern C++" rejält om man kör "by value", går inte det ska man absolut konsekvent använda std::unique_ptr<> eller std::shared_ptr<>. std::shared_ptr<> är kanske enklare för nybörjare då det blir mer likt hur man jobbar i språk med GC (man får dock akta sig för cirkel-referenser).

Vidare är just minneskopiering något moderna CPUer är exceptionellt snabba på så länge det handlar om "rimliga" storlekar. Mellan tummen och pekfingret kan man säga att alla minneskopieringar upp till en "page" (4-16 kB) är i praktiken "gratis", eller mer konkret: om sådana kopieringar kan förenkla koden är nettoeffekten ofta att prestanda går upp. Så även det är en orsak att föredra "by-value".

Såg en kommentar i en C++ vs Rust diskussion som jag först trodde var ett skämt, men visade sig i senare kommentarer att personen var helt alvarlig. Hen menade på att Rust "borrow-checker" var helt onödig för man kan ju göra samma sak "i huvudet" när man jobbar i C++ och det blir "i princip" alltid rätt... Grejen är att när det blir fel har man saker som use-after-free, double-free och, än värre, data-race. D.v.s. bland de absolut värsta buggarna att försöka hitta i live-system.

Med all respekt, om det bara "nästan" alltid blir rätt med new/delete, så låter det snarare som att användaren saknar erfarenhet av det. Man kan alltid göra rätt med new/delete, även om jag ser vad som lockar med uniqueptr givet ägarskap. Att felsöka problem som beror på felaktig användning av pekare är busenkelt om man har rätt erfarenhet. Att rekommendera användare att alltid frånstå vanlig minnesallokering i C++ för att språket har mycket högre inlärningskurva än ex. C# skadar bara språket i sig.

Argumenten du listar upp låter som samma typ av argument som C#/UE-nissar använder när dom förklarar varför multiple inheritance, virtual inheritance, diamond-pattern inheritance etc. är "dåliga". dvs för att det är svårt för en oerfaren att använda dom rätt.

Har man erfarenhet av C++ så är new/delete inte ett sämre alternativ till unique_ptr i alla lägen, eller ens i dom flesta lägen.

Nulla dina pekare vid initializering och delete, kolla dina pekare innan användning, och får du problem så använd din debugger rätt och kolla hur minnet ser ut. Har inte fastnat på pekarproblem sen mina första år med C++, det är barnmat för oss som använt det dagligen.

Permalänk
Datavetare
Skrivet av herkkä:

Med all respekt, om det bara "nästan" alltid blir rätt med new/delete, så låter det snarare som att användaren saknar erfarenhet av det. Man kan alltid göra rätt med new/delete, även om jag ser vad som lockar med uniqueptr givet ägarskap. Att felsöka problem som beror på felaktig användning av pekare är busenkelt om man har rätt erfarenhet. Att rekommendera användare att alltid frånstå vanlig minnesallokering i C++ för att språket har mycket högre inlärningskurva än ex. C# skadar bara språket i sig.

Argumenten du listar upp låter som samma typ av argument som C#/UE-nissar använder när dom förklarar varför multiple inheritance, virtual inheritance, diamond-pattern inheritance etc. är "dåliga". dvs för att det är svårt för en oerfaren att använda dom rätt.

Har man erfarenhet av C++ så är new/delete inte ett sämre alternativ till unique_ptr i alla lägen, eller ens i dom flesta lägen.

Nulla dina pekare vid initializering och delete, kolla dina pekare innan användning, och får du problem så använd din debugger rätt och kolla hur minnet ser ut. Har inte fastnat på pekarproblem sen mina första år med C++, det är barnmat för oss som använt det dagligen.

Så "lösningen" är att använda det uppfinnaren av null anser vara en av datorvärldens största misstag?

"I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."

Även om du råkar vara den enda människa i världen som aldrig får detta fel så hjälper inte det i lite större projekt därför att det kommer finnas andra och de kommer göra fel om det är möjligt att göra fel! Rätt lösning är då att plocka bort människan från flödet. Datorprogram (kompilatorer) behöver man bara få till rätt en gång, sedan kommer datorn se till att det blir rätt varje gång framöver

Permalänk
Medlem
Skrivet av Yoshman:

Så "lösningen" är att använda det uppfinnaren av null anser vara en av datorvärldens största misstag?

"I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years."

Även om du råkar vara den enda människa i världen som aldrig får detta fel så hjälper inte det i lite större projekt därför att det kommer finnas andra och de kommer göra fel om det är möjligt att göra fel! Rätt lösning är då att plocka bort människan från flödet. Datorprogram (kompilatorer) behöver man bara få till rätt en gång, sedan kommer datorn se till att det blir rätt varje gång framöver

Ser din poäng och respekterar den ur en praktisk synpunkt. Jag tycker ju dock att vi bör sätta ribban högre. Klart jag också får segfaults och annat roligt lite nu och då under utvecklingsfasen, som dock blir åtgärdade på nolltid. Men det är ju därför man testar sin produkt gedigen innan man släpper den också. Buggar (och även derefs av null) händer med unique_ptr också.

Jag är helt ok om folk vill använda unique_ptr. Och jag använder själv shared_ptr väldigt väl där det gör nytta. Har bara ett stort problem med att vissa utvecklare uppmanar nybörjare att "ooh använd aldrig new/delete, det är i praktiken deprecated, använd den här hajpade STL mallen istället" bara för att dom själva föredrar att alltid använda smarta pekare. Det gör bara att dessa utvecklare blir mindre bildade och ökar förmodligen risken för att dom av oerfarenhet ändå klantar till minneshanteringen.

Lär folk hur man använder båda. new/delete kommer inte försvinna någonstans, hela språket bygger på det konceptet, och std::unique_ptr är bara ett hjälpmedel. Vill man ha bättre inbyggd säkerhet till kostnad för begränsad funktionalitet så bör man kika på C#.

Permalänk
Datavetare
Skrivet av herkkä:

Ser din poäng och respekterar den ur en praktisk synpunkt. Jag tycker ju dock att vi bör sätta ribban högre. Klart jag också får segfaults och annat roligt lite nu och då under utvecklingsfasen, som dock blir åtgärdade på nolltid. Men det är ju därför man testar sin produkt gedigen innan man släpper den också. Buggar (och även derefs av null) händer med unique_ptr också.

Jag är helt ok om folk vill använda unique_ptr. Och jag använder själv shared_ptr väldigt väl där det gör nytta. Har bara ett stort problem med att vissa utvecklare uppmanar nybörjare att "ooh använd aldrig new/delete, det är i praktiken deprecated, använd den här hajpade STL mallen istället" bara för att dom själva föredrar att alltid använda smarta pekare. Det gör bara att dessa utvecklare blir mindre bildade och ökar förmodligen risken för att dom av oerfarenhet ändå klantar till minneshanteringen.

Lär folk hur man använder båda. new/delete kommer inte försvinna någonstans, hela språket bygger på det konceptet, och std::unique_ptr är bara ett hjälpmedel. Vill man ha bättre inbyggd säkerhet till kostnad för begränsad funktionalitet så bör man kika på C#.

👍

Då är vi nästan överens. Håller absolut inte med om det sista, C# är inte på något sätt en förbättring över "modern" C++ på den punkten. Rust är, än så länge, unikt i att faktiskt ha en "lösning" på flera (dock långt i från alla) av de riktigt kniviga problem vi fightats med.

För TS är nog ändå inte Rust, utan som redan föreslagits i tråden, något typ Python eller liknande nog rätt val för just detta problem.

Permalänk
Medlem
Skrivet av Yoshman:

👍

Då är vi nästan överens. Håller absolut inte med om det sista, C# är inte på något sätt en förbättring över "modern" C++ på den punkten. Rust är, än så länge, unikt i att faktiskt ha en "lösning" på flera (dock långt i från alla) av de riktigt kniviga problem vi fightats med.

Du har förmodligen rätt på den fronten. C# skaver rent generellt för mig men det är väl den approachen det språket försöker ta. Rust har jag ännu inte grävt mig ner i, får hitta ett hobbyprojekt för det snart!