Chirurgia endoscopică a hipofizei
"Standardul de aur" în chirurgia hipofizară îl reprezintă endoscopia transnazală transsfenoidală. Echipa NeuroHope este antrenată în unul din cele mai mari centre de chirurgie a hipofizei din Europa, Spitalul Foch din Paris, centrul în care a fost introdus pentru prima dată endoscopul în chirurgia transnazală a hipofizei, de către neurochirurgul francez Guiot. Pe lângă tumorile cu origine hipofizară, prin tehnicile endoscopice transnazale pot fi abordate numeroase alte patologii neurochirurgicale. www.neurohope.ro |
De la C la C++
Last Updated: Jul 18 2013 10:21, Started by
dani.user
, Jun 16 2013 17:46
·
0
#1
Posted 16 June 2013 - 17:46
De la C la C++
Știu că pare ciudat, dar voi incepe acest tutorial printr-o negare: NU, a programa în C++ nu înseamnă a folosi cout în loc de printf. E ca și cum am apela la un TIR pentru a muta 5 kg de nisip dintr-o curte in alta. Acest tutorial are ca scop stârnirea curiozității în ceea ce privește C++ și clarificarea unor des întâlnite confuzii. Pentru însușirea limbajului e nevoie de multă lectură + exerciții, un punct de plecare fiind http://www.cplusplus.com/doc/tutorial/ . Întâi a fost puiul, apoi a urmat găina Limbajul C a apărut înaintea lui C++. Să ne amintim ce curpinde:
... și cam atât. Toate acestea, împreună cu biblioteciile puse la dispoziție de sistemul de operare și, opțional, de terți, ne permit să facem cam orice. A putea face însă orice, nu înseamnă însă că e și ușor, sau productiv când lucrurile se complică. Limitări ale limbajului C:
Programarea orientată pe obiect sau un alt mod de a vedea lucrurile C fiind un limbaj destul de low-level, ne face să ne gândim întotdeauna la ce se petrece la nivel de memorie, chiar de instructiuni. Nu e nimic rău în asta, mai ales când învățăm, dar ce ne facem atunci când lucrăm la o aplicație complexă, a cărei multitudine de cerințe ne dă destul de gândit încât să nu mai dorim să ne batem capul la fiecare pas și cu reprezentarea lucrurilor în memorie? Programarea orientată pe obiect (POO, OOP) se bazează foarte mult pe gândirea în termenii problemei de rezolvat și nu a modului de funcționare al calculatorului. Ea ne face să gândim lucrurile în termen de componente, fiecare rezolvând o anumită problemă, iar utilizarea combinată a acestor componente ne va aduce soluția la problema noastră. Fie următoarea cerință: Quote
Să se scrie un program care citește de pe un site, al cărui nume este primit din partea utilizatorului, valoarea cursului valutar al EURO pentru data curentă și care stochează această valoare într-un fișier. În C o astfel de abordare ar arăta în felul următor: struct ConsoleReader reader; struct HTTPDownloader downloader; struct ExchangeRateParser parser; struct FileWriter writer; init_ConsoleReader(&reader); init_HTTPDownloader(&downloader); init_ExchangeRateParser(&parser); init_File_Writer(&writer, "euro.txt"); char* siteURL = ConsoleReader_prompUser(&reader); char* siteContent = HTTPDownloader_get(&downloader, siteURL); double euroExchangeRate = ExchangeRateParser_extract(&parser, siteContent, "euro"); FileWriter_write(&writer, euroExchangeRate); Astfel avem o structură pentru fiecare obiect și o gramadă de funcții ce operează cu date din aceste structuri, fiind astfel necesar să transmitem de fiecare dată un pointer spre structura în cauză. C++ simplifică lucrurile permitând definirea funcțiilor în interiorul structurilor/claselor, funcții ce vor primi un prim argument ascuns de tip pointer spre instanța structurii/clasei în cauză, argument denumit this. De asemenea o structură/clasă în C++ poate avea 1..n funcții de inițializare (constructor) și o funcție de curățenie (destructor). Pe lângă această simplificare, C++ asigură consistenţa datelor din obiect. Acestea vor fi întotdeauna inițializate corect și vor păstra valori valide pentru logica obiectului, grație codului scris de dezvoltatorul obiectului. În caz, această sarcină cădea parțial și în sarcina utilizatorului, acesta trebuind să fie atât șofer cât și mecanic. Structura FileWriter poate fi implementată în C++ în felul următor: struct FileWriter { private: FILE* file; public: FileWriter(const char* name) //constructor, are loc la creerea unei noi instanțe a obiectului { this->file = fopen(name, 'w'); //this poate fi omis, compilatorul intelegand despre ce e vorba ... //verificare daca fisierul s-a deschis cu succes, etc } ~FileWriter() //destructor, functie executata la distrugerea instanței obiectului { ... //verificare daca file e valid, etc... fclose(fisier); } void write(const char* value) { ... //verificare daca file e valid, etc... fprintf(this->file, "%s", value); } } Atunci când se dorește alocare dinamică a memoriei în C++, nu se va folosi malloc()/free() deoarece acestea doar alocă memorie pentru o structură, nu și apelează funcțiile constructor/destructor. Se vor folosi nou introdușii operatori new respectiv delete: FileWriter* fileWriter = new FileWriter("a.txt"); ... delete fileWriter; Astfel, o posibilă rezolvare a acestei probleme în C++ arată în felul următor: ConsoleReader reader; HTTPDownloader downloader; ExchangeRateParser parser; FileWriter writer("euro.txt"); String siteURL = reader.promptUser(); String siteContent = downloader.get(siteURL); double euroExchangeRate = parser.extract(siteContent, "euro"); writer.write(euroExchangeRate); Avem astfel 4 clase care fiecare rezolvă o parte a problemei, iar împreună ne rezolvă întreaga problemă. Ce e important de observat și reținut e că aceste clase indeplinesc fiecare câte o singură problemă și că nu sunt legate între ele (putând fiecare din ele să fie folosită și într-o altă aplicație - reutilizarea codului):
În POO se pune foarte mult accent și pe abstractizare. Astfel eu poate nici nu știu cum se descarcă conținutul unui site, componenta HTTPDownloader fiind scrisă de altcineva. Mi-e suficient însă să știu ce face această clasă și cum să o folosesc pentru a-mi rezolva treaba cu ajutorul ei. Quote
Bun bun, dar dacă nu ne gândim la aspectele low-level, aplicațiile nu vor mai rula optim, va dura mai mult execuția lor! Parțial adevărat. Lucrurile în aplicațiile "concrete" sunt mult mai complexe decât în cazul problemelor de liceu/olimpiade, în cazul cărora, alegerea unui algoritm greșit poate face diferența dintre rezolvarea unei probleme în 0.5 secunde sau în 15 secunde. Dar hai să analizăm mai atent problema de mai sus. În acest caz, se poate observa foarte repede că cea mai lentă etapă va fi descărcarea efectivă a informațiilor, să presupunem că ar dura o secundă. Urmează extragerea efectivă a valorii cursului valutar. Să presupunem că cel mai eficient algoritm extrage acea valoare în 1 ms, pe când unul mai rudimentar o va face în 10 ms. Vorbim astfel de un timp de 10x mai lung, dar contează totuși? Per total avem o diferență între 1.001 ms VS 1.010 ms, adică o nimica toată. Morala: înainte de a investi timp în găsirea/implementarea unui algoritm mai eficient, trebuie văzut dacă căștigul adus este pe măsura timpului investit. TEMĂ PROPUSĂ Să se scrie o clasă String pentru lucrul cu șiruri de caractere, clasă ce va permite să scriu următorul cod: ... String a; a = "Hello "; int sizeA = a.length(); char litera = a[1]; //e String b("World"); String c = a + b; //Hello World c += " din C++"; String d = c.subString(3); //lo World din C++ printf("%s", c.toCharPointer()); ... Clasa se va găsi în namespace-ul MyLib și va stoca intern datele folosind un char* cu acces privat. Edited by dani.user, 17 July 2013 - 18:01. |
#2
Posted 16 June 2013 - 18:13
Înainte de punctul "Protecția membriilor structurilor" ai putea să bagi un punct care explică de ce structurile sunt atât de importante (i.e. o definiţie şi eventual showcase a noţiunii de "obiect" construită doar cu noţiuni din C, via function pointers). Ar face mai lesne de înţeles de ce în punctul ulterior, "Protecția membriilor structurilor", ţi se pune pata tocmai pe structuri de date.
Ar fi şi procedura mai naturală de a explica de unde a apărut nevoia de OOP - ataşarea unui comportament unor date între care există o relaţie "supraetajată", şi nu cum cred novicii pe dos: "gruparea de funcţii". După paragraful "C++ simplifică lucrurile permitând" aş mai pune un paragraf de genul: Pe lângă această simplificare, C++ asigură consistenţa datelor din obiect. |
#4
Posted 19 June 2013 - 09:35
Adaug cateva idei:
1. tot ce se poate face in C++ se poate face si in C, chiar daca mai greu de aceea... pct 2. 2. nu e obligatoriu sa invete cineva intai C, apoi C++. Se poate incepe si ramane linistit doar cu C++ chiar daca e mai greu. |
#5
Posted 20 June 2013 - 17:36
1. Nu doar mai greu ci si mai susceptibil la erori. Cand ai un proiect mare care-ti crapa aleator, ti-ai dori din tot sufletul sa fi primit defapt ceva erori de compilare.
2. Pana nu intelege & stapaneste treaba cu obiectele, se poate minti toata ziua ca invata C++, ca defapt tot C invata, chiar daca foloseste cout in loc de printf. |
#6
Posted 20 June 2013 - 17:42
#8
Posted 20 June 2013 - 17:53
#9
Posted 20 June 2013 - 18:06
Având în vedere că lumea a intrat în vacanță, propun următorul exercițiu:
Oricine e liber să propună soluții, important e să se discute pe seama lor. |
#10
Posted 22 June 2013 - 19:10
Pe un topic cerea cineva o soluție pentru implementarea operațiilor cu polinoame în C++.
Majoritatea ar defini o structură pentru a ține coeficienții polinomului, sau poate nici atât, ar declara un vector de numere și l-ar boteza "polinom1", iar apoi ar scrie tot felul de funcții gen aduna_polinoame, imparte_polinoame etc. Iar apoi ar mai face uz de cout (că doar suntem în C++), iar, ca tacâmul să fie complet, ar apela cout direct din funcția de adunare, pentru a afișa rezulatul... Quote Ain't that cute ... but it's wrooooong Pai dacă tot avem avantajul că ni se cere o rezolvare în C++, de ce să nu beneficiem la maxim de ce ne pune C++-ul pe tavă, și să creem o clasă Polinom care să ne facă, în primul rând nouă, plăcere să o refolosim ori de câte ori mai avem de rezolvat câte o problemă ce implică polinoame? OK ... să începem atunci. Primul, și poate cel mai important, aspect legat de această problemă este că, în lumea C++, atunci când adunăm două lucruri, ne place să scriem a + b indiferent că a și b sunt numere, mașini, sau, în acest caz, polinoame. Prin urmare clasa, pe care o vom crea, ar fi fain să o putem folosi la modul următor: #include <iostream> #include "polinom.h" using namespace std; int main() { Polinom a(1, 1, 4, 2, 1); //1x^4+2x^3+4x^2+1x+1 Polinom b(-1, 1); //1x-1 cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "a + b = " << (a + b) << endl; cout << "a - b = " << (a - b) << endl; cout << "a * 5 = " << (a * 5) << endl; cout << "a / 5 = " << (a / 5) << endl; cout << "a * b = " << (a * b) << endl; cout << "a / b = " << (a / b) << " REST " << (a % b) << endl; return 0; } Astfel avem o mulțime de avantaje:
Codul necesar pentru a implementa o astfel de clasă este prezentat mai jos: polinom.h #ifndef POLINOM_H #define POLINOM_H #include <iostream> class Polinom { public: Polinom(double a, double b = 0, double c = 0, double d = 0, double e = 0, double f = 0, double g = 0, double h = 0); Polinom(const Polinom& p); unsigned int rang() const; double& operator[](unsigned int index) const; Polinom operator=(const Polinom& p); Polinom operator+(const Polinom& p) const; Polinom operator-(const Polinom& p) const; Polinom operator*(const double v) const; Polinom operator*(const Polinom& p) const; Polinom operator/(const double v) const; Polinom operator/(const Polinom& p) const; Polinom operator%(const Polinom& p) const; virtual ~Polinom(); friend std::ostream& operator<<(std::ostream& s, const Polinom& p); protected: double* valori; unsigned int nrValori; private: }; std::ostream& operator<<(std::ostream& s, const Polinom& p); #endif // POLINOM_H polinom.c #include <cmath> #include "polinom.h" using namespace std; Polinom::Polinom(double a, double b, double c, double d, double e, double f, double g, double h) { //alocam memorie pentru valori valori = new double[nrValori = 8]; valori[0] = a; valori[1] = b; valori[2] = c; valori[3] = d; valori[4] = e; valori[5] = f; valori[6] = g; valori[7] = h; } Polinom::Polinom(const Polinom& p) { //copiem alt polinom valori = new double[nrValori = p.nrValori]; for (unsigned int i = 0; i < nrValori; i++) { valori[i] = p[i]; } } Polinom::~Polinom() { //stergem vectorul alocat dinamic delete valori; } unsigned int Polinom::rang() const { for (int i = nrValori - 1; i >= 0; i--) { if (abs(valori[i]) > 0.0001) //valorile double nu se compara cu == { return i; } } return 0; } Polinom Polinom::operator=(const Polinom& p) { delete valori; //copiem alt polinom valori = new double[nrValori = p.nrValori]; for (unsigned int i = 0; i < nrValori; i++) { valori[i] = p[i]; } return *this; } double zero; double& Polinom::operator[](unsigned int index) const { zero = 0; if (index >= nrValori) return zero; return valori[index]; } Polinom Polinom::operator+(const Polinom& p) const { Polinom rezultat(*this); //copie a polinomului curent for (unsigned int i = 0; i < max(this->nrValori, p.nrValori); i++) { rezultat[i] += p[i]; } return rezultat; } Polinom Polinom::operator-(const Polinom& p) const { Polinom rezultat(*this); //copie a polinomului curent for (unsigned int i = 0; i < max(this->nrValori, p.nrValori); i++) { rezultat[i] -= p[i]; } return rezultat; } Polinom Polinom::operator*(const double v) const { Polinom rezultat(*this); //copie a polinomului curent for (unsigned int i = 0; i <= this->rang(); i++) { rezultat[i] *= v; } return rezultat; } Polinom Polinom::operator*(const Polinom& p) const { Polinom rezultat(0); //copie a polinomului curent for (unsigned int i = 0; i <= this->rang(); i++) { for (unsigned int j = 0; j <= p.rang(); j++) { rezultat.valori[i + j] += valori[i] * p[j]; } } return rezultat; } Polinom Polinom::operator/(const double v) const { return *this * (1.0 / v); } Polinom Polinom::operator/(const Polinom& p) const { Polinom curent(*this); Polinom rezultat(0); Polinom temp(0); while(curent.rang() >= p.rang()) { temp = Polinom(0); temp[curent.rang() - p.rang()] = curent[curent.rang()] / p[p.rang()]; rezultat = rezultat + temp; curent = curent - temp * p; } return rezultat; } Polinom Polinom::operator%(const Polinom& p) const { return *this - p * (*this / p); } ostream& operator<<(ostream& s, const Polinom& p) { bool totiCoeficientiiZero = true; bool primulCoeficient = true; for (int i = p.nrValori - 1; i >= 0; i--) { double coeficient = p.valori[i]; if (abs(p[i]) > 0.0001) //valorile double nu se compara cu == { totiCoeficientiiZero = false; if ( coeficient > 0 && ! primulCoeficient) { s << "+"; } if (i == 0) { s << coeficient; } else if (p[i] > 0 && abs(p[i] - 1.0) > 0.0001) { s << coeficient; } else if (abs(p[i] + 1.0) < 0.0001) { s << "-"; } primulCoeficient = false; if (i > 0) { s << "x"; if (i > 1) { s << "^" << i; } } } } if (totiCoeficientiiZero) { s << 0; } return s; } Ei, v-am stârnit interesul? Vedeți ce înseamnă o problemă rezolvată în C++? Înțelegeți de ce oftăm când vedem cerințe în C++, iar soluțiile nu seamănă deloc cu asta? Clasa de mai sus nu este perfectă. Odată ce a-ți înteles conceptele folosite, o puteți îmbunătăți:
|
|
#11
Posted 22 June 2013 - 20:08
Aş mai adăuga nişte matematică cu sens:
dani.user, on 22 iunie 2013 - 19:10, said:
Clasa de mai sus nu este perfectă. Odată ce a-ți înteles conceptele folosite, o puteți îmbunătăți:
|
#12
Posted 23 June 2013 - 17:31
Codul nu stie sa imparta cu polinomul nul, deci am modificat rang() sa returneze -1 pt. polinomul nul (defapt prin conventie e -inf, dar aici conventia mea e -1) si verific ca nu cumva sa am de a face cu polinomul nul sau cu numarul 0 cand fac impartirile si daca am primit asa ceva ca argument arunc o exceptie:
#include <stdexcept> int Polinom::rang() const { for (int i = nrValori - 1; i >= 0; i--) { if (abs(valori[i]) > 0.0001) //valorile double nu se compara cu == { return i; } } return -1; } Polinom Polinom::operator/(const double v) const { if(0 == v){ throw invalid_argument("Impartire cu zero!"); } return *this * (1.0 / v); } Polinom Polinom::operator/(const Polinom& p) const { if(-1 == p.rang()){ throw invalid_argument("Impartire cu polinomul nul!"); } Polinom curent(*this); Polinom rezultat(0); Polinom temp(0); while(curent.rang() >= p.rang()) { temp = Polinom(0); temp[curent.rang() - p.rang()] = curent[curent.rang()] / p[p.rang()]; rezultat = rezultat + temp; curent = curent - temp * p; } return rezultat; } Evident ca am modificat header-ul si toate locurile unde aparea rang() ca sa nu fac comparatii intre signed si unsigned, in rest nu cred ca am afectat cu ceva codul. De asemenea o sugestie pt. cei ce vor folosi clasa asta mai departe e sa optimizeze rang() facand o memoizare. Si acum o intrebare, ce ati modifica voi sau ce ctor ati adauga ca sa permiteti instantierea unui polinom de orice grad, nu neparat <=8? Eu as scapa de ctor-ul curent (care ar fi prost pt. backwards compatibility) si as face unul care primeste un std::vector<T> (cu asta am impuscat si template-ul), ce ziceti? Un exercitiu bun ar putea fi si implementarea unei clase pt. numere complexe, pt. a aplica ce ne-a aratat aici dani.user. PS: Nu stiu ce face forumul, dar face ceva, ca la mine in editor e perfect indentat codul, aici, e ca la nebuni. Edited by Paullik, 23 June 2013 - 18:01. |
#13
Posted 24 June 2013 - 07:03
Paullik, on 23 iunie 2013 - 17:31, said:
Eu as scapa de ctor-ul curent (care ar fi prost pt. backwards compatibility) si as face unul care primeste un std::vector<T> (cu asta am impuscat si template-ul), ce ziceti? Paullik, on 23 iunie 2013 - 17:31, said:
PS: Nu stiu ce face forumul, dar face ceva, ca la mine in editor e perfect indentat codul, aici, e ca la nebuni. |
#14
Posted 24 June 2013 - 20:51
OriginalCopy, on 24 iunie 2013 - 07:03, said:
Un iterator e şi mai sus pe scala abstractizării. Intr-adevar daca ma gandesc la STL, metodele erase, insert, etc. primesc iteratori. OriginalCopy, on 24 iunie 2013 - 07:03, said:
Clic pe butonul "Cod", paste cod, press OK, şi apoi nu te mai atingi de el. Se dă peste cap dacă modifici direct. Edited by Paullik, 24 June 2013 - 20:52. |
#15
Posted 24 June 2013 - 21:16
Uita-te mai atent la algoritmi generici ce accepta iteratori diversi.
Gandeste-te si la o metoda sa pot initializa un polinom cu n valori fara sa creez inainte un vector/array. |
|
#16
Posted 24 June 2013 - 21:59
dani.user, on 24 iunie 2013 - 21:16, said:
Uita-te mai atent la algoritmi generici ce accepta iteratori diversi. dani.user, on 24 iunie 2013 - 21:16, said:
Gandeste-te si la o metoda sa pot initializa un polinom cu n valori fara sa creez inainte un vector/array. Polinom f; f<<5<<0<<0<<1; //5x^3 + 1 Nu? Edited by Paullik, 24 June 2013 - 22:05. |
#17
Posted 25 June 2013 - 17:57
http://www.cplusplus...ric/accumulate/
A face overload la << e aiurea fiindca il poti folosi si dupa ce initializezi polinomul, creand un comportament aiurea. Apoi clasa a fost gandita pentru a fi immutable. |
#18
Posted 12 July 2013 - 21:53
Observ ca mentionezi programarea orientata obiect, dar nu povestesti despre ea si nu exemplifici
De asemenea, incerci sa vorbesti despre prea multe lucruri deodata, iar structura are de suferit. Altfel, felicitari pentru initiativa! |
Anunturi
▶ 1 user(s) are reading this topic
0 members, 1 guests, 0 anonymous users