Table of Contents

Podstawy programowania 2

Wykłady

Wykłady odbywają się w środy o 19:00 na platformie MS Teams.

Link do zespołu

Kod do zespołu zostanie przekazany staroście

  1. Przeciążanie funkcji i operatorów [12.04.2023, 26.04.2023]
  2. Wyjątki [10.05.2023]
  3. Kontenery i iteratory [10.05.2023(wektor) + ]

Egzamin

Podczas egzaminu: * można korzystać z dowolnych materiałów w formie fizycznej (podręczników, notatek, itp.) * nie można wymieniać się materiałami, podawać notatek sąsiadom, itp. * nie można korzystać z telefonów

Proszę przynieść własne kartki formatu A4, np. 3 na odpowiedzi oraz 3 na brudnopis i 2 długopisy.

Uwagi 2023

trg.a=towary[i].a;
trg.b=towary[i].b;
trg.c=towary[i].c;
trg.d=towary[i].d;

Wystarczy trg=towary[i]

Uwagi

Uwaga 1

Jeżeli tematem było napisanie szablonów klas typu Set<T> lub Wielozbiór<T> z zastrzeżeniami dotyczącymi dostępnych operatorów dla klasy będącej parameterem szablonu, to zapewne nie można było użyć std::set<T> lub std::map<T>.

Uwaga 2

Szablony unordered_set lub unordered_map działają na klasach “haszowalnych”, tzn. takich dla których da się obliczyć wartość indeksu w tablicy na podstawie zawartości.

Proszę spróbować skompilować:

class A{
public:
    A(int _v):v(_v){}
    int v;
    bool operator==(const A&a)const{
        return v==a.v;
    }
};
 
int main(){
    std::unordered_set<A> set;
    set.insert(A(1));
    std::cout<<(set.find(A(1))!=set.end())<<std::endl;
}

Dla nietypowej własnej klasy należy dostarczyć własny obiekt funkcyjny do obliczania “hasza” (czyli położenia w tablicy)…

class A{
public:
    A(int _v):v(_v){}
    int v;
    bool operator==(const A&a)const{
        return v==a.v;
    }
};
 
class MyHashForA{
public:
    size_t operator()(const A&a)const{
        return std::hash<int>{}(a.v);
    }
};
int main(){
    std::unordered_set<A,MyHashForA> set;
    set.insert(A(1));
    std::cout<<(set.find(A(1))!=set.end())<<std::endl;
}

Uwaga 3

Funkcja nie może zwracać wskaźnika po dereferencji do obiektu, dla którego pamięć przydzielono na stercie. Jak zwolnić tę pamięć? Memory leak…

class A{
public:
    int v;
};
 
A bardzo_zle_napisana_funkcja(){
    A*ptr = new A();
    ptr->v=10;
    printf("ptr=%p\n",ptr);
    return *ptr;
}
 
int main(){
    A a1 = bardzo_zle_napisana_funkcja();
    printf("&a1=%p\n",&a1);
    A a2;
    a2=bardzo_zle_napisana_funkcja();
    printf("&a2=%p\n",&a2);
    // jak zwolnić ptr?
}
 
// Wynik
ptr=0x80004add0
&a1=0xffffcc3c
ptr=0x80004ae40
&a2=0xffffcc38

Planowane zadania

Zasady oceny

Za co będą odliczane punkty

Bez STL

Użyj STL

Laboratoria, grupy 2a i 2b

:!: Proszę zatwierdzać test. Jest na to 10 minut po upływie czasu

Laboratorium 11

sizeof(text) ma wartość 8 (na platformie 64-bitowej). Długość tekstu obliczamy za pomocą strlen()

TextListElement::TextListElement(const char *txt) {
    value = new char[sizeof(txt)];
    strcpy(value, txt);
    next = 0;
    prev = 0;
}

Lab 10 Dirent

Directory&Directory::operator=(const Directory&other){
    if(&other!=this){
        free();
        copy(other);
    }
    return *this;
}
void Directory::free(){
 
     for (auto e:entries)delete e;
     entries.clear();
}
void Directory::copy(const Directory&other){
    this->name = other.name;
    // NIE: this->parent = other.parent;
    this->parent = nullptr; // Obiekt nardrzędny ustawi wskaźnik w instrukcji (2) 
 
    //(1) kopiowanie
    for(auto e:other.entries){
       // entries.push_back( kopia e)
    }
    // (2) 
    for(auto e:entries){
        e->parent=this;
    }
}

Downcasting?

Poniższe rozwiązanie jest poprawne, ale dyskusyjne… Nieporzebnie stosowane jest rzutowanie w dół hierarchii. Klasa u szczytu hierarchii musiałaby znać wszystkie klasy potomne.

Dirent *Dirent::find(const char *name) {
    if (is_dir()) {
        for (auto element : dynamic_cast<Directory *>(this)->entries) {
            if (element->name == name) {
                return element;
            } else if (element->is_dir()) {
                if (element->find(name)) {
                    return element->find(name);
                }
            }
 
 
        }
 
 
    }
}

Zamiast tego (jeżeli decydujemy się na wprowadzenie do interfejsu klasy bazowej):

// zadeklarowane jako virtual 
Dirent *Dirent::find(const char *_name){
  if (name==_name ) return this;
  return nullptr;
}
 
 
Dirent *Directory::find(const char *_name) {
  if (name==_name ) return this;
  // lub:
  //auto e = Dirent::find(_name);
  //if(e) return e;
 
  for (auto element : entries) {
       auto e = e->find(_name);
       if(e)return e;     
  }
  return nullptr;//0
}

Po co downcasting w przypadku funkcji wirtualnych?

    for (auto& e:entries) {
        if(e->is_dir()) dynamic_cast<Directory*>(e)->list(os, indent);
        if(e->is_file()) dynamic_cast<File*>(e)->list(os, indent);
    }

Lab 9 - String

bool String::operator==(const char*_txt)const{
    if(_txt == this->txt) return true; // BŁĄD
    return false;
}
String operator+(Arg arg){
  String ret(*this);
  ret += arg;
  return ret;
}

Lab 7 Relacja

    auto domain  = get_domain();
    auto range = get_range();
    set<int> all(domain);
    for(auto e:range)all.insert(e);
for(int i=0;i<pairs.size();i++){
   if(has_pair(pairs[i].x,pairs[i].y)...
} 
bool relation::is_antisymmetric()const{
    for(auto&e:pairs){
        if(e.x!=e.y && has_pair(e.y,e.x))return false;
    }
    return true;
}
class relation{
public:
    class pair{
    public:
        int x;
        int y;
        bool operator<(const pair&other)const;
        pair(int _x, int _y):x(_x),y(_y){}
    };
public:
#if defined USE_VECTOR
    vector<pair> pairs;
#endif
#if defined USE_SET
    set<pair> pairs;
#endif
    ... itd

dwie zmiany:

bool relation::has_pair(int x,int y)const{
#if defined USE_VECTOR
    for(int i=0;i<pairs.size();i++){
        if(pairs[i].x==x && pairs[i].y==y)return true;
    }
    return false;
#endif
#if defined USE_SET
    auto it = pairs.find(pair(x,y));
    return it!=pairs.end();
#endif
 
void relation::add(int x,int y){
#if defined USE_VECTOR
    if(!has_pair(x,y))pairs.emplace_back(x,y);
#endif
#if defined USE_SET
    pairs.insert(pair(x,y));
#endif
}

Przy zmianie struktur danych żadna inna funkcja nie wymagała modyfikacji

Lab 5/6 Kretowisko

- Kretowisko II

Lab 4

Po to napisaliśmy metodę Student::write, aby jej użyć, a nie pisać jak poniżej:

void StudentList::write(ostream&os)const{
    for(int i=0;i<count;i++){
        os<<tablica[i].indeks<<'\t'<<tablica[i].imie<<'\t'<<tablica[i].nazwisko<<'\t';
        os<<tablica[i].skreslony<<'\t'<<tablica[i].grupa<<'\t';
    }
}

Lab 2,3

Styl

if(ok1){
   if(ok2){
      if(ok3){
          // Tu dobry ciąg instrukcji...
      }else{
 
      }
   }else{
 
   }
}else {
 
}

… nikt nie sprawdzi tych warunków else

Algorytmy

U prawie wszystkich linspace() wygeneruje wyjątek dla n=1

Funkcje

Lab 1

Logika zabezpieczeń

void Fibo1::init(int _n)
{
    if(_n<=0)tab=0;
    n = _n;
    tab = new int[n];
}
 
void Fibo1::destroy()
{
    delete [] tab; 
}
 
    ArithmeticSequence(int _n, double _r, double _first)
    {
 
        if(_n<=0)return; // USTAW n=0, tab=0 
        n=_n;
        r=_r;
        first=_first;
        tab = new double[n];
    }
 
void Fibo1::init(int n) {
    if(n <= 0) {
        tab = 0;
    }
    tab = new int[n];
}
 
void Fibo1::init(int _n)
{
    n = _n; 
    int* tab = new int[_n]; 
    if(n <= 0){ tab = NULL; }
}
 
 
 
Fibo::~Fibo(){
    if(n>0) delete []tab; // TO JEST POPRAWNE, ale proponuję raczej idiom if(tab) delete []tab;
}

Class::member służy do czegoś innego…

    for (int _i = 0; _i < ArithmeticSequence::n; _i++)
    {
        cout << ArithmeticSequence::arr[_i] << endl;
    }
 
void Fibo1::fill()
{
    tab[0]=1,tab[1]=1; // a jak tab==0 lub n=1?
    for(int i=2;i<n;i++) tab[i]=tab[i-1]+tab[i-2];
}

https://stackoverflow.com/questions/34614869/using-scope-operator-to-access-non-static-member-variables

Bez średnika!

#define N 10;

To nie jest stała, to jest VLA

Variable Length Arrays nie należą do standardu C++. Zamiast tego należy używać vector<int>

void fibo1(){
int n;
int tab[n];