portaldacalheta.pt
  • Põhiline
  • Veebi Kasutajaliides
  • Ui Disain
  • Andmeteadus Ja Andmebaasid
  • Vilgas
Tagumine Ots

Kümme kõige levinumat C ++ viga, mida arendajad teevad



On palju lõkse, mis a C ++ arendaja võivad kokku puutuda. See võib muuta kvaliteetse programmeerimise väga raskeks ja hoolduse väga kulukaks. Keele süntaksi õppimisest ja headest programmeerimisoskustest sarnastes keeltes, nagu C # ja Java, lihtsalt ei piisa C ++ kogu potentsiaali kasutamiseks. Vigade vältimiseks C ++ -s on vaja aastatepikkust kogemust ja suurt distsipliini. Selles artiklis vaatleme mõningaid levinumaid vigu, mida kõigi tasandite arendajad teevad, kui nad pole C ++ arendusega piisavalt ettevaatlikud.

Üldine viga nr 1: paaride „uus” ja „kustutamine” kasutamine on vale

Ükskõik kui palju me ka ei prooviks, on kogu dünaamiliselt eraldatud mälu väga raske vabastada. Isegi kui suudame seda teha, pole see sageli erandite eest kaitstud. Vaatame lihtsat näidet:



void SomeMethod() { ClassA *a = new ClassA; SomeOtherMethod(); // it can throw an exception delete a; }

Kui visatakse erand, siis objekti 'a' ei kustutata kunagi. Järgmine näide näitab turvalisemat ja lühemat viisi. See kasutab funktsiooni auto_ptr, mis on versioonis C ++ 11 aegunud, kuid vana standardit kasutatakse endiselt laialdaselt. Võimaluse korral saab selle asendada C ++ 11 unique_ptr või scoped_ptr saidilt Boost.



void SomeMethod() { std::auto_ptr a(new ClassA); // deprecated, please check the text SomeOtherMethod(); // it can throw an exception }

Ükskõik, mis juhtub, pärast objekti 'a' loomist kustutatakse see kohe, kui programmi täitmine ulatusest väljub.



See oli aga lihtsalt kõige lihtsam näide sellest C ++ probleemist. On palju näiteid, kui kustutamine peaks toimuma mõnes teises kohas, võib-olla välise funktsiooni või muu lõimena. Seetõttu tuleks täielikult vältida uue / kustutamise kasutamist paarides ja selle asemel tuleks kasutada sobivaid nutikaid näpunäiteid.

Üldine viga nr 2: unustatud virtuaalne hävitaja

See on üks levinumaid vigu, mis põhjustab tuletatud klasside sees mälulekkeid, kui nende sees on eraldatud dünaamiline mälu. Mõnel juhul pole virtuaalne hävitaja soovitav, st kui klass pole mõeldud pärimiseks ja selle suurus ja jõudlus on üliolulised. Virtuaalne destruktor või mõni muu virtuaalne funktsioon toob klassi struktuuri sisse täiendavaid andmeid, s.o virtuaalse tabeli kursori, mis muudab klassi mis tahes eksemplari suuremaks.



Enamasti saab klassid pärida ka siis, kui see pole algselt ette nähtud. Seega on klassi deklareerimisel virtuaalse hävitaja lisamine väga hea tava. Vastasel juhul, kui klass ei tohi jõudlusega seotud põhjustel sisaldada virtuaalseid funktsioone, on hea tava lisada klassi deklaratsioonifaili kommentaar, mis näitab, et klassi ei tohiks pärida. Üks parimatest võimalustest selle probleemi vältimiseks on IDE kasutamine, mis toetab klassi loomise ajal virtuaalse hävitaja loomist.

Üheks lisapunktiks õppeainele on standardraamatukogu klassid / mallid. Need pole mõeldud pärimiseks ja neil puudub virtuaalne hävitaja. Näiteks kui loome uue täiustatud stringiklassi, mis pärib avalikult std :: stringist, on võimalus, et keegi kasutab seda kursori või viitega std :: stringile valesti ja põhjustab mälulekke.



class MyString : public std::string { ~MyString() { // ... } }; int main() { std::string *s = new MyString(); delete s; // May not invoke the destructor defined in MyString }

Selliste C ++ probleemide vältimiseks on tavalisest raamatukogust klassi / malli korduvkasutamiseks turvalisem viis kasutada privaatset pärimist või koosseisu.

Üldine viga nr 3: massiivi kustutamine „kustutamise” abil või nutika kursori kasutamine

Üldine viga nr 3

kuidas arvutada aktsia ipo hinda

Dünaamilise suurusega ajutiste massiivide loomine on sageli vajalik. Pärast seda, kui neid enam ei nõuta, on oluline eraldatud mälu vabastada. Suur probleem on see, et C ++ nõuab spetsiaalset [] sulgudega kustutusoperaatorit, mis ununeb väga lihtsalt. Operaator delete [] ei kustuta lihtsalt massiivi jaoks eraldatud mälu, vaid kutsub kõigepealt massiivi kõigi objektide hävitajad. Samuti on vale kasutada kustutamisoperatsiooni ilma [] sulgudeta primitiivsete tüüpide jaoks, kuigi nende tüüpide jaoks pole hävitajat. Iga kompilaatori jaoks pole garantiid, et massiivi osuti osutab massiivi esimesele elemendile, seega võib sulgudeta [] kustutamise kasutamine põhjustada ka määratlemata käitumise.

Nutikate osutite (nt auto_ptr, unique_ptr, shared_ptr) kasutamine massiividega on samuti vale. Kui selline tark osuti ulatusest väljub, kutsub ta kustutamisoperaatori ilma sulgudeta, mis toob kaasa samad probleemid, mida on kirjeldatud eespool. Kui massiivi jaoks on vajalik nutika kursori kasutamine, on võimalik kasutada funktsiooni scoped_array või shared_array from Boost või unikaalset_ptr spetsialiseerumist.

Kui võrdlusloendamise funktsionaalsust ei nõuta, mis enamasti juhtub massiivide puhul, on kõige elegantsem viis kasutada hoopis STL-vektoreid. Nad ei hoolitse ainult mälu vabastamise eest, vaid pakuvad ka täiendavaid funktsioone.

Üldine viga nr 4: kohaliku objekti tagastamine viitena

See on enamasti algaja viga, kuid tasub seda mainida, kuna selle probleemi all kannatab palju pärandkoode. Vaatame järgmist koodi, kus programmeerija soovis tarbetut kopeerimist vältides teha mingisugust optimeerimist:

Complex& SumComplex(const Complex& a, const Complex& b) { Complex result; ….. return result; } Complex& sum = SumComplex(a, b);

Objekt „summa“ osutab nüüd kohalikule objektile „tulemus“. Aga kus asub objekti „tulemus” pärast funktsiooni SumComplex käivitamist? Mitte kusagil. See asus virnas, kuid pärast funktsiooni tagastamist pakiti virn lahti ja kõik funktsiooni kohalikud objektid hävitati. Selle tulemuseks on lõpuks isegi primitiivsete tüüpide määratlemata käitumine. Jõudlusprobleemide vältimiseks on mõnikord võimalik kasutada tagastusväärtuse optimeerimist.

Complex SumComplex(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imaginar + b.imaginar); } Complex sum = SumComplex(a, b);

Kui tagasirida sisaldab objekti konstruktorit, optimeeritakse enamiku tänapäevaste kompilaatorite jaoks kood, et vältida tarbetut kopeerimist - konstruktor käivitatakse otse objektil „summa“.

Üldine viga nr 5: viite kasutamine kustutatud ressursile

Neid C ++ -probleeme juhtub sagedamini, kui arvate, ja neid nähakse tavaliselt mitmikeermelistes rakendustes. Vaatleme järgmist koodi:

1. lõim:

Connection& connection= connections.GetConnection(connectionId); // ...

Teema 2:

connections.DeleteConnection(connectionId); // …

1. lõim:

connection.send(data);

Selles näites, kui mõlemad lõimed kasutasid sama ühenduse ID-d, põhjustab see määratlemata käitumise. Juurdepääsurikkumise vigu on sageli väga raske leida.

w2 vs 1099 võrdluskalkulaator

Sellistel juhtudel, kui samale ressursile pääseb juurde rohkem kui üks lõim, on ressurssidele viiteid või viiteid hoida väga riskantne, sest mõni muu lõime võib selle kustutada. Palju turvalisem on kasutada viite loendamise abil nutikaid näpunäiteid, näiteks Boostilt jagatud_ptr. Ta kasutab võrdlusloenduri suurendamiseks / vähendamiseks aatomioperatsioone, nii et see on niidile ohutu.

Üldine viga nr 6: erandite lubamine hävitajatele lahkumiseks

Destruktori erandit pole sageli vaja visata. Isegi siis on selleks parem viis. Kuid enamasti ei visata hävitajaid erandeid selgesõnaliselt. Võib juhtuda, et lihtne käsk objekti hävitamise registreerimiseks põhjustab erandi viskamise. Vaatame järgmist koodi:

class A { public: A(){} ~A() { writeToLog(); // could cause an exception to be thrown } }; // … try { A a1; A a2; } catch (std::exception& e) { std::cout << 'exception caught'; }

Kui ülaltoodud koodis esineb erand kaks korda, näiteks mõlema objekti hävitamise ajal, ei täideta püügilauset kunagi. Kuna paralleelselt on kaks erandit, olenemata sellest, kas need on sama tüüpi või erinevat tüüpi, ei tea C ++ käituskeskkond seda käsitseda ja kutsub lõpetamise funktsiooni, mille tulemuseks on programmi täitmise lõpetamine.

Nii et üldreegel on: ärge kunagi lubage eranditel hävitajaid jätta. Isegi kui see on kole, tuleb potentsiaalset erandit kaitsta nii:

try { writeToLog(); // could cause an exception to be thrown } catch (...) {}

Tavaline viga nr 7: „auto_ptr” kasutamine (valesti)

Automaatne_ptr mall on C ++ 11-st vananenud mitmel põhjusel. Seda kasutatakse endiselt laialdaselt, kuna enamikku projekte arendatakse endiselt versioonis C ++ 98. Sellel on teatud omadus, mis pole ilmselt kõigile C ++ arendajatele tuttav ja võib põhjustada tõsiseid probleeme kellelegi, kes pole ettevaatlik. Auto_ptr objekti kopeerimine annab omandiõiguse ühelt objektilt teisele üle. Näiteks järgmine kood:

auto_ptr a(new ClassA); // deprecated, please check the text auto_ptr b = a; a->SomeMethod(); // will result in access violation error

... toob kaasa juurdepääsuõiguste rikkumise vea. Ainult objekt „b” sisaldab kursorit A-klassi objektile, samas kui „a” on tühi. Objekti “a” klassi liikmele juurdepääsu proovimine toob kaasa juurdepääsuõiguste tõrke. Auto_ptr-i valeks kasutamiseks on palju võimalusi. Neli väga kriitilist asja, mida nende kohta meeles pidada, on:

  1. Ärge kunagi kasutage auto_ptr STL-i konteinerites. Konteinerite kopeerimisel jäävad lähtemahutid kehtetute andmetega. Mõni STL-algoritm võib põhjustada ka „auto_ptr” -de kehtetuks muutmise.

  2. Ärge kunagi kasutage funktsiooni argumendina auto_ptr, kuna see viib kopeerimiseni ja jätke argumendile edastatud väärtus pärast funktsiooni väljakutset kehtetuks.

  3. Kui klassi andmeliikmete jaoks kasutatakse funktsiooni auto_ptr, tehke kindlasti koopiaehitaja ja määranguoperaatori sisse korralik koopia või lubage need toimingud privaatseks muutes.

    mis tüüpi veebipõhine rünnak kasutab html-vormi hankimise ja postitamise funktsioone?
  4. Võimaluse korral kasutage auto_ptr asemel mõnda muud kaasaegset nutikat osutit.

Üldine viga nr 8: kehtetute iteraatorite ja viidete kasutamine

Sellel teemal oleks võimalik kirjutada terve raamat. Igal STL-i konteineril on mõned spetsiifilised tingimused, mille korral see muudab iteraatorid ja viited kehtetuks. Mis tahes toimingu tegemisel on oluline neist üksikasjadest teadlik. Täpselt nagu eelmine C ++ probleem, võib ka see ilmneda väga sageli mitmikeermelistes keskkondades, nii et selle vältimiseks on vaja kasutada sünkroonimismehhanisme. Vaatame näiteks järgmist järjestikukoodi:

vector v; v.push_back(“string1”); string& s1 = v[0]; // assign a reference to the 1st element vector::iterator iter = v.begin(); // assign an iterator to the 1st element v.push_back(“string2”); cout << s1; // access to a reference of the 1st element cout << *iter; // access to an iterator of the 1st element

Loogilisest vaatenurgast tundub kood täiesti korras. Teise elemendi lisamine vektorisse võib aga põhjustada vektori mälu ümberjaotamise, mis muudab nii iteraatori kui ka viite kehtetuks ning toob kaasa juurdepääsu rikkumise vea, kui proovite neile juurde pääseda kahel viimasel real.

Üldine viga nr 9: objekti edastamine väärtuse järgi

Üldine viga nr 9

Tõenäoliselt teate, et on halb mõte objekte väärtuse kaudu edasi anda, kuna see mõjutab jõudlust. Paljud jätavad selle lisamärkide sisestamise vältimiseks niimoodi või mõtlevad tõenäoliselt optimeerimise tegemiseks hiljem tagasi tulla. Tavaliselt seda kunagi ei tehta ja selle tulemuseks on vähem sooritatud kood ja ootamatu käitumise suhtes kalduv kood:

class A { public: virtual std::string GetName() const {return 'A';} … }; class B: public A { public: virtual std::string GetName() const {return 'B';} ... }; void func1(A a) { std::string name = a.GetName(); ... } B b; func1(b);

See kood kompileeritakse. Funktsiooni „func1“ kutsumine loob objekti „b“ osalise koopia, see tähendab, et see kopeerib ainult objekti „A“ klassi „b“ osa objektile „a“ („viilutamise probleem“). Nii et funktsiooni sees kutsub see ka B-klassi meetodi asemel meetodit klassist A, mis tõenäoliselt ei ole funktsiooni kutsuva inimese eeldatav.

Sarnased probleemid tekivad ka erandite püüdmisel. Näiteks:

class ExceptionA: public std::exception; class ExceptionB: public ExceptionA; try { func2(); // can throw an ExceptionB exception } catch (ExceptionA ex) { writeToLog(ex.GetDescription()); throw; }

Kui funktsioonist “func2” visatakse erand, mille tüüp on ExceptionB, visatakse see püüdmisplokile, kuid viilutamisprobleemi tõttu kopeeritakse ainult osa klassist ExceptionA, kutsutakse vale meetod ja visatakse uuesti viskab vale erandi väljastpoolt proovimise püüdmisplokki.

Kokkuvõtteks edastage objektid alati viitena, mitte väärtuse järgi.

Levinud viga nr 10: Konstruktori ja konversioonioperaatorite poolt kasutaja määratud konversioonide kasutamine

Isegi kasutaja määratud konversioonid on mõnikord väga kasulikud, kuid need võivad viia ennustamatute konversioonideni, mida on väga raske leida. Oletame, et keegi lõi raamatukogu, millel on stringiklass:

class String { public: String(int n); String(const char *s); …. }

Esimene meetod on mõeldud stringi loomiseks pikkusega n ja teine ​​on mõeldud antud märke sisaldava stringi loomiseks. Kuid probleem algab kohe, kui teil on midagi sellist:

String s1 = 123; String s2 = ‘abc’;

Ülaltoodud näites saab s1-st string suurusega 123, mitte string, mis sisaldab märke '123'. Teine näide sisaldab jutumärke topelt jutumärkide asemel (mis võib juhtuda kogemata), mille tulemusel kutsutakse ka esimene konstruktor ja luuakse väga suure suurusega string. Need on tõesti lihtsad näited ja on palju keerulisemaid juhtumeid, mis põhjustavad segadust ja ettenägematuid konversioone, mida on väga raske leida. Selliste probleemide vältimiseks on 2 üldreeglit:

  1. Määrake implitsiitsete konversioonide keelamiseks selgesõnalise märksõnaga konstruktor.

    kuidas krediitkaarti häkkida
  2. Konversioonioperaatorite asemel kasutage selgesõnalisi vestlusmeetodeid. See nõuab natuke rohkem kirjutamist, kuid seda on palju puhtam lugeda ja see aitab vältida ettearvamatuid tulemusi.

Järeldus

C ++ on võimas keel. Tegelikult on paljud rakendused, mida igapäevaselt arvutis kasutate ja armunud olete, tõenäoliselt ehitatud C ++ abil. Keelena annab C ++ a tohutult paindlikkust arendajale läbi kõige keerukamate funktsioonide, mida on näha objektorienteeritud programmeerimiskeeltes. Need keerukad funktsioonid või paindlikkus võivad aga paljude arendajate jaoks tekitada segadust ja pettumust, kui neid vastutustundlikult ei kasutata. Loodetavasti aitab see loend teil mõista, kuidas mõned neist tavalistest vigadest mõjutavad seda, mida saate C ++ abil saavutada.

Seotud: C- ja C ++ -keelte õppimine: ülim nimekiri

Kuidas luua SSO-nupp - kolbi sisselogimise õpetus

Tagumine Ots

Kuidas luua SSO-nupp - kolbi sisselogimise õpetus
Parem kasutajasõbralikkus mikrosuhtluse kaudu

Parem kasutajasõbralikkus mikrosuhtluse kaudu

Ux Disain

Lemmik Postitused
Raha kogumise pigi teki kunst
Raha kogumise pigi teki kunst
Amazon vs. Walmart: Bezos läheb kogu toiduainete omandamisega jugulaarseks
Amazon vs. Walmart: Bezos läheb kogu toiduainete omandamisega jugulaarseks
Bootstrapped: kaugettevõtte ehitamine
Bootstrapped: kaugettevõtte ehitamine
Bootstrapi kasutamine ja .NET-projektide loomine
Bootstrapi kasutamine ja .NET-projektide loomine
Kuidas luua meilisõnumite analüüsi bot: NLP-õpetus.
Kuidas luua meilisõnumite analüüsi bot: NLP-õpetus.
 
Kommunikatsioonidirektor
Kommunikatsioonidirektor
Kuidas vältida funktsioonide libisemist kasutajalugude parimate tavade abil
Kuidas vältida funktsioonide libisemist kasutajalugude parimate tavade abil
Täpsem Git-juhend: Git Stash, Reset, Rebase ja palju muud
Täpsem Git-juhend: Git Stash, Reset, Rebase ja palju muud
Disainihariduse tähtsus
Disainihariduse tähtsus
KPI-d edu saavutamiseks - ülevaade projektijuhi jõudlusmõõdikutest
KPI-d edu saavutamiseks - ülevaade projektijuhi jõudlusmõõdikutest
Lemmik Postitused
  • tehnoloogia külmas sõjas
  • teil on kasutaja, kellel on probleeme oma töö salvestamisega. mida peab olema ettevaatlik, et mitte kahe silma vahele jätta?
  • lepingust täistööajaga palga muutmine
  • küberturvalisus kolledži ülikoolilinnakutes
  • c korporatsioon ja s korporatsioon
  • päringu optimeerimine sql serveris 2012
Kategooriad
  • Veebi Kasutajaliides
  • Ui Disain
  • Andmeteadus Ja Andmebaasid
  • Vilgas
  • © 2022 | Kõik Õigused Kaitstud

    portaldacalheta.pt