Kurs podstawy programowania UMCS, zdjęcie laptopa.

PP – Laboratorium 12

Zadanie 1

Napisz program w języku C++, który: dodaje, odejmuje, mnoży, dzieli dwie liczby zespolone oraz oblicza sprzężenie zespolone. Następnie program powinien wyświetlić wyniki tych operacji.

Rozwiązanie:
#include <iostream>
#include <complex>

int main() {
    std::complex<double> z1(1.0, 3.0);
    std::complex<double> z2(1.0, -4.0);

    std::complex<double> sum = z1 + z2;
    std::complex<double> difference = z1 - z2;
    std::complex<double> product = z1 * z2;
    std::complex<double> quotient = z1 / z2;
    std::complex<double> conjugate = std::conj(z1);

    std::cout << "Real part(z1): " << std::real(z1) << " Imaginary part(z1): " << std::imag(z1) << std::endl;
    std::cout << sum << " " << difference << " " << product << " " << quotient << " " << conjugate << std::endl;

    return 0;
}

Zadanie 2 – typ wyliczeniowy enum

Napisz program w języku C++, który wczyta od użytkownika liczbę całkowitą. Następnie program powinien wyświetlić:

  • Left, gdy użytkownik wprowadził 0;
  • Right, gdy użytkownik wprowadził 1;
  • Up, gdy użytkownik wprowadził 2;
  • Down, gdy użytkownik wprowadził 3;
  • Anywhere dla każdej innej wartości wprowadzonej przez użytkownika.
Rozwiązanie:
#include <iostream>

enum Direction{LEFT, RIGHT, UP, DOWN};

int main() {
    Direction s;//operator >> nie jest przeciążony dla enum-a mimo, że jest to zmienna która przyjmuje wartości całkowite dlatego musisz robić to inaczej.
    int value;
    std::cin >> value;
    s = static_cast<Direction>(value);
    switch(s) {
        case LEFT: std::cout << "Left" << std::endl; break;
        case RIGHT: std::cout << "Right" << std::endl; break;
        case UP: std::cout << "Up" << std::endl; break;
        case DOWN: std::cout << "Down" << std::endl; break;
        default: std::cout << "Anywhere" << std::endl;
    }
    return 0;
}
Omówienie:

Typ wyliczeniowy to rodzaj danych, który zawiera listę wartości, które może przyjąć zmienna własnego typu wyliczeniowego. Innymi słowy jest to typ definiowany przez użytkownika, zawierający określony zbiór stałych liczbowych typu całkowitego wraz z przypisanymi im etykietami (stałymi symbolicznymi, enumeratorami). Taki typ poprzedza słowo kluczowe enum. W celu oddzielenia kolejnych wartości, które można przypisać zmiennej tego typu, używa się przecinków. Typ wyliczeniowy przechowuje wartości, jako liczby całkowite (zakres typu int), o czym można przekonać się wyświetlając zmienną typu wyliczeniowego:
Direction s = DOWN;
std::cout << s;


Zaleca się, aby podczas definiowania wartości typu wyliczeniowego przy ostatnim elemencie nie używać przecinka.


Precyzując kolejne wartości typu wyliczeniowego to liczby całkowite: domyślnie pierwsza wartość to 0, druga to 1 itd. Oczywiście przy deklarowaniu typu wyliczeniowego jest możliwość zmiany domyślnego przyporządkowania wartości:

enum Direction {LEFT = 8, RIGHT, UP, DOWN};
std::cout << LEFT << " " << RIGHT << " " << UP << " " << DOWN << std::endl;
lub
std::cout << Direction(LEFT) << " " << Direction(RIGHT) << " " << Direction(UP) << " " << Direction(DOWN) << std::endl;

Powyższy program wyświetli:
8 9 10 11
Co więcej liczby mogą się powtarzać i wcale nie muszą być ustawione w kolejności rosnącej:

enum Direction {LEFT = 5, RIGHT = 5, UP = 2, DOWN = -1};
std::cout << LEFT << " " << RIGHT << " " << UP << " " << DOWN << std::endl;

Powyższy program wyświetli:
5 5 2 -1
Traktowanie przez kompilator typu wyliczeniowego jako liczby pozwala na wydajną ich obsługę, ale stwarza niebezpieczeństwa. Można przypisywać pod typ wyliczeniowy liczby, nawet nie mające odpowiednika w wartościach, a kompilator może o tym nawet nie ostrzec:

enum Direction {LEFT, RIGHT, UP = -1, DOWN};

Co spowoduje nadanie tej samej wartości 0 dla elementów LEFT i DOWN, a to może skutkować błędem kompilacji, np. przy użyciu instrukcji switch.

Zadanie 3

Napisz program w języku C++, który będzie symulował nadawanie praw dostępu plików tj. Rreadable, Wwritable, Xexecutable.

Rozwiązanie:
#include <iostream>

enum Privileges{R = 4, W = 2, X = 1};

int main() {
    int mod = R + X;
    std::cout << mod << std::endl;
    if(mod & R) std::cout << "readable\n";
    if(mod & W) std::cout << "writable\n";
    if(mod & X) std::cout << "executable\n";
    return 0;
}

Zadanie 4

Zdefiniuj typ wyliczeniowy składający się z wartości odzwierciedlających typy: CHAR, UCHAR, INT, FLOAT. Napisz funkcję w języku C++, która przyjmie jako argumenty wskaźnik na void, pod którym znajduje się tablica, rozmiar tej tablicy oraz zmienną uprzednio zdefiniowanego typu wyliczeniowego oznaczającą typ danych znajdujących się w tablicy. Funkcja powinna zwrócić średnią arytmetyczną danych w przekazanej tablicy za pomocą typu float. Napisz program w języku C++, który przetestuje działanie tej funkcji.

Rozwiązanie:
#include <iostream>
enum Type{CHAR, UCHAR, INT, FLOAT};
float avg(void *arr, int n, Type t) {
    float sum = 0;
    switch(t) {
        case CHAR: {
            char *sarr = (char*)arr;
            for(int i = 0; i < n; ++i) sum += sarr[i];
            break;
        }
        case UCHAR: {
            unsigned char *sarr = (unsigned char*)arr;
            for(int i = 0; i < n; ++i) sum += sarr[i];
            break;
        }
        case INT: {
            int *sarr = (int*)arr;
            for(int i = 0; i < n; ++i) sum += sarr[i];
            break;
        }
        case FLOAT: {
            float *sarr = (float*)arr;
            for(int i = 0; i < n; ++i) sum += sarr[i];
            break;
        }
    }
    return sum / n;
}
int main() {
    unsigned char arr1[5] = {50, 100, 150, 200, 250};
    std::cout << avg(arr1, 5, UCHAR) << std::endl;
    float arr2[5] = {50, 100, 150, 200, 250};
    std::cout << avg(arr2, 5, FLOAT) << std::endl;
    return 0;
}

Zadanie 5 – struktury

Napisz program w języku C++, który zdefiniuje typ złożony reprezentujący punkt w dwuwymiarowej przestrzeni euklidesowej. Punkt powinien być wyznaczony za pomocą dwóch współrzędnych. Następnie wczytaj od użytkownika współrzędne dwóch punktów. Oblicz i wyświetl odległość pomiędzy tymi punktami.

Rozwiązanie:
//Version 1.0
#include <iostream>
#include <cmath>

struct Point{
    float x, y;
};

int main() {
    Point a, b;

    std::cin >> a.x >> a.y >> b.x >> b.y;
    std::cout << sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)) << std::endl;

    return 0;
}
//Version 2.0
#include <iostream>
#include <cmath>

struct Point{
    float *x, *y;
};

int main() {
    Point a, b;
    float a_v = 0, b_v = 2;
    a.x = a.y = &a_v; 
    b.x = b.y = &b_v;
    std::cout << sqrt(pow(*a.x - *b.x, 2) + pow(*a.y - *b.y, 2)) << std::endl;

    return 0;
}
//Version 3.0
#include <iostream>
#include <cmath>

struct Point{
    float *x, *y;
};

int main() {
    Point a, b;
    float a_v, b_v;
    a.x = a.y = &a_v;
    b.x = b.y = &b_v;
    *a.x = 0;
    *b.y = 2;
    std::cout << sqrt(pow(*a.x - *b.x, 2) + pow(*a.y - *b.y, 2)) << std::endl;

    return 0;
}
Omówienie:

Strukturę definiujemy w podobny sposób do typu wyliczeniowego enum. Struktury to złożone typy danych pozwalające przechowywać różne informacje. Za pomocą struktur możliwe jest grupowanie wielu zmiennych o różnych typach w jeden obiekt. Definicję struktury rozpoczynamy od słowa kluczowego struct, następnie podajemy nazwę struktury i w klamrach definiujemy ciało struktury (np. zmienne). Należy pamiętać, że po klamrze zamykającej definicje musi następować średnik. Brak tego średnika jest częstym błędem powodującym czasami niezrozumiałe komunikaty błędów.

Omawiana struktura posiada dwie zmienne (pola) x i y. Ten element kodu nazywamy atrybutami lub składowymi. Aby odwołać się do atrybutu zmiennej typu złożonego używamy .(kropki) lub -> dla zmiennych wskaźnikowych (patrz Zadanie 7).

Zadanie 6 – tablice struktur

Napisz program w języku C++, który umożliwi przechowywanie czterech elementów, typu zdefiniowanego w poprzednim zadaniu, o dowolnych współrzędnych. Następnie program powinien obliczyć i wyświetlić obwód figury wskazanej przez te punkty.

Rozwiązanie:
#include <iostream>
#include <cmath>

struct Point{
    float x, y;
};

int main() {
    Point arr[4]={1.f,1.f, 1.f,2.f, 2.f,2.f, 2.f,1.f};
    //lub Point arr[4]={{1.f,1.f}, {1.f,2.f}, {2.f,2.f}, {2.f,1.f}};
    float sum = 0;
    for(int i = 0; i < 4; ++i)
        sum += sqrt(pow(arr[i].x - arr[(i + 1) % 4].x, 2) + pow(arr[i].y - arr[(i + 1) % 4].y, 2));
    std::cout << sum << std::endl;
    return 0;
}

Zadanie 7 – struktury jako argumenty funkcji i dynamiczna alokacja obiektów

Napisz program w języku C++, który zdefiniuje typ złożony reprezentujący wektor za pomocą trzech współrzędnych kartezjańskich XYZ. Napisz funkcje w języku C++, które przyjmują trzy wskaźniki na ten typ v1, v2 i result. Funkcje powinny obliczyć: a) sumę wektorów i b) różnicę wektorów.

Rozwiązanie:
#include <iostream>

struct Vector3D{
    float x, y, z;
};

void add(Vector3D *v1, Vector3D *v2, Vector3D *result) {
    result->x = v1->x + v2->x;
    result->y = v1->y + v2->y;
    result->z = v1->z + v2->z;
}

void sub(Vector3D *v1, Vector3D *v2, Vector3D *result) {
    result->x = v1->x - v2->x;
    result->y = v1->y - v2->y;
    result->z = v1->z - v2->z;
}

int main() {
    Vector3D *v1 = new Vector3D;
    Vector3D *v2 = new Vector3D;
    v1->x = v1->y = v1->z = 3;
    v2->x = v2->y = v2->z = 1;

    Vector3D *result = new Vector3D;
    add(v1, v2, result);
    std::cout << result->x << " " << result->y << " " << result->z << std::endl;
    sub(v1, v2, result);
    std::cout << result->x << " " << result->y << " " << result->z << std::endl;

    /*
    lub nie używając alokacji dynamicznej
    Vector3D vv1 = {3, 3, 3};
    Vector3D vv2 = {1, 1, 1};
    Vector3D v_result;
    sub(&vv1, &vv2, &v_result);
    std::cout << v_result.x << " " << v_result.y << " " << v_result.z << std::endl;
    */

    delete v1;
    delete v2;
    delete result;
    return 0;
}
Omówienie:

Struktury jako argumenty funkcji mogą być przekazywane na dwa sposoby:
– przekazywanie przez wartość,
– przekazywanie przez referencję (wskaźnikiem lub referencją).
Podsumowując przekazywanie obiektów struktur, jako argumenty funkcji odbywa się tak samo, jak w przypadku innych zmiennych typów prostych.

Zadanie 8

Napisz program w języku C++, który umożliwi przechowywanie wyników poprzedniego zadania w tablicy. Użyj do tego dynamicznej alokacji pamięci. Dodatkowo gdy wektory mają takie same współrzędne funkcje obliczające sumę lub różnicę wektorów powinny zwrócić wektor (0,0,0).

Rozwiązanie:
#include <iostream>
#include <cstring>

struct Vector3D {
    float x, y, z;
};

void add(Vector3D *v1, Vector3D *v2, Vector3D* result) {
    if( memcmp(v1, v2, sizeof(Vector3D)) != 0) {
        result->x = v1->x + v2->x;
        result->y = v1->y + v2->y;
        result->z = v1->z + v2->z;
    } else result->x = result->y = result->z = 0;
}

void sub(Vector3D *v1, Vector3D *v2, Vector3D* result) {
    if( memcmp(v1, v2, sizeof(Vector3D)) != 0) {
        result->x = v1->x - v2->x;
        result->y = v1->y - v2->y;
        result->z = v1->z - v2->z;
    } else result->x = result->y = result->z = 0;
}

int main() {
    Vector3D *v1 = new Vector3D;
    Vector3D *v2 = new Vector3D;
    v1->x = v1->y = v1->z = 3;
    v2->x = v2->y = v2->z = 1;

    Vector3D **result = new Vector3D*[2];
    for(int i = 0; i < 2; ++i) result[i] = new Vector3D;
    add(v1, v2, result[0]);
    sub(v1, v2, result[1]);
    for(int i = 0; i < 2; ++i)
        std::cout << result[i]->x << " " << result[i]->y << " " << result[i]->z << std::endl;

    delete v1;
    delete v2;
    for(int i = 0; i < 2; ++i) delete result[i];
    delete[] result;
    return 0;
}

Zadanie 9

Napisz program w języku C++, który zdefiniuje typ złożony Person mający dwa pola: liczbę całkowitą – age, napis – name. Program powinien stworzyć obiekt tego typu, a następnie stworzyć jego głęboką kopię.

Rozwiązanie:
//Version 1.0
#include <iostream>
#include <cstring>

struct Person{short age; char* name;};

int main() {
    const char *emil = "Emil";
    Person p1;
    p1.age = 20;
    p1.name = new char[24];
    strcpy(p1.name, emil); //strcpy(p1.name, "Emil");

    Person p2 = p1;
    std::cout << "Age: " << p2.age << ", Name: " << p2.name << std::endl;  //To ten sam adres p1.name i p2.name
    delete[] p1.name;
    std::cout << "Age: " << p2.age << ", Name: " << p2.name << std::endl;  //Błąd
    return 0;
}
//Version 2.0
#include <iostream>
#include <cstring>

struct Person{short age; char* name;};

int main() {
    const char *emil = "Emil";
    Person p1;
    p1.age = 20;
    p1.name = new char[24];
    strcpy(p1.name, emil); //strcpy(p1.name, "Emil");

    Person p2 = p1;
    p2.name = new char[24]; //Teraz to już inny adres niż p1.name
    strcpy(p2.name, p1.name);
    std::cout << "Age: " << p2.age << ", Name: " << p2.name << std::endl;  
    delete[] p1.name;
    std::cout << "Age: " << p2.age << ", Name: " << p2.name << std::endl;  
    return 0;
}
Omówienie:

Ważne: Przy przypisaniu obiektów struktur, które zawierają wskaźniki, kopiowany jest wskaźnik (adres) a nie obiekt, na który wskazuje!
Podsumowując głębokiej kopii używamy, gdy klasa lub struktura obiektu, który chcemy skopiować ma pola referencyjne. Ma to na celu uniknięcie sytuacji, w której składowe referencyjne obiektów klas lub struktur wskazują na ten sam obszar w pamięci.

Zadanie 10

Napisz program w języku C++, który będzie symulował jednokierunkową listę wskaźnikową wartości dowolnego typu.

Rozwiązanie:
#include <iostream>

struct Node {
    void *data;
    Node *next;
};

void push_front(Node **head_ref, void *new_data, size_t data_size) {
    Node *new_node = new Node;
    new_node->data = new char[data_size];
    for(size_t i = 0; i < data_size; ++i)
        *((char *)new_node->data + i) = *((char *)new_data + i);

    new_node->next = *head_ref;
    *head_ref = new_node;
}

void print_list(Node *node, void (*fptr)(void *)) {
    while(node != nullptr) {
        fptr(node->data);
        node = node->next;
    }std::cout << "\n";
}

void print_int(void *data) {
    std::cout << *(int *)data << " ";
}

void print_float(void *data) {
    std::cout << *(float *)data << " ";
}

int main() {
    Node *start = nullptr;
    unsigned int int_size = sizeof(int);
    int arr1[] = {10, 20, 30, 40, 50}, i;
    for(i = 4; i >= 0; --i)
        push_front(&start, &arr1[i], int_size);
    print_list(start, print_int);

    start = nullptr;
    unsigned int float_size = sizeof(float);
    float arr2[] = {10.1f, 20.2f, 30.3f, 40.4f, 50.5f};
    for(i = 4; i >= 0; --i)
        push_front(&start, &arr2[i], float_size);
    print_list(start, print_float);

    return 0;
}

Zadanie 11 – klasy

Zaprojektuj klasę Point, która posiada dwa pola typu zmiennoprzecinkowego x, y. Następnie napisz funkcję w języku C++, która przyjmuje tablicę obiektów typu Point i jej rozmiar. Funkcja w argumencie będącym wskaźnikiem na obiekt klasy Point powinna zwrócić współrzędne punkty, który jest położony najbliżej punktu (0,0).

Rozwiązanie:
#include <iostream>
#include <cmath>

class Point {
public:
    float x, y;
};

float get_distance(const Point &p) {
    return sqrt(p.x * p.x + p.y * p.y);
}

void f(Point *arr, int n, Point *&p) {
    float cur_distance, min_distance = get_distance(arr[0]);
    int id = 0;
    for(int i = 1; i < n; ++i) {
        cur_distance = get_distance(arr[i]);
        if(min_distance > cur_distance) {
            min_distance = cur_distance;
            id = i;
        }
    }
    p = arr + id;
}

int main() {
    Point arr[5] = {0.2, 0.3f, 1.f, 1.5f, 2.f, 2.2f, 0.1f, 0.1f, 3.f, 3.2f};
    Point *p;
    f(arr, 5, p);
    std::cout << "(" << p->x << ":" << p->y << ")\n";
    return 0;
}
Omówienie:

W C/C++ class i struct są prawie synonimami. Jednakże są pomiędzy nimi pewne różnice, jedną z nich jest fakt, że class ma pola domyślnie prywatne, zaś struct ma pola domyślnie publiczne. Mówiąc inaczej, gdybyśmy stworzyli klasę i nie napisali, że zmienne powinny być publiczne, to przy odwołaniu się do niepublicznego atrybutu lub metody otrzymali byśmy błąd.

Przykład:
class Point {
    float x;
public:
    float y;
};

int main() {
    Point p;
    p.y = 1; // OK
    p.x = 1; // Błąd
    return 0;
}

Słowa kluczowe public, private i protected służą do określenie praw dostępu do atrybutów i metod klas.

Słowo kluczowe private oznacza dostęp do zmiennych i funkcji tylko z poziomu klasy. Mówiąc precyzyjniej, dostęp do tych elementów jest możliwy jedynie z poziomu metod tej klasy. Atrybuty i metody prywatne nie są widoczne poza klasą. Czym są metody oraz kwestie dostępu do atrybutów klas omówimy szerzej w kolejnych wpisach tego kursu.

Słowo kluczowe protected ma podobne własności co słowo kluczowe private. Różnice pojawiają się tylko wtedy, gdy przyjdzie nam dziedziczyć klasy. Jeśli będziemy dziedziczyli klasę A do klasy B to zmienne oznaczone jako private w klasie A będą niewidoczne dla klasy B. Inaczej będzie dla praw dostępu protected, wtedy zmienne i funkcje klasy A będą widoczne dla klasy B.

Słowo kluczowe public
Dostęp publiczny umożliwia uzyskiwanie dostępu do dowolnej zmiennej i funkcji z dowolnego miejsca w kodzie.

Zadanie 12

Zaprojektuj klasę Point, która posiada dwa pola typu zmiennoprzecinkowego x, y. Następnie napisz funkcję, która przyjmuje tablicę obiektów typu Point i jej rozmiar, a także trzy liczby rzeczywiste a, b i r, będące parametrami następującego równania okręgu (x - a)2 + (y - b)2 = r2. Funkcja powinna zwrócić, za pomocą argumentu, wskaźnik na tablicę wskaźników na punkty położne w zdefiniowanym kole.

Rozwiązanie:
#include <iostream>
#include <cmath>

class Point {
public:
    float x, y;
};

float get_distance(const Point &p, float x, float y) {
    return sqrt(pow(p.x - x, 2) + pow(p.y - y, 2));
}

int count_points_in_circle(Point *arr, int n, float a, float b, float r) {
    int count = 0;
    for(int i = 0; i < n; ++i)
        if(r >= get_distance(arr[i], a, b))
            count++;
    return count;
}

void f(Point *arr, int n, float a, float b, float r, Point **result) {
    for(int i = 0, j = 0; i < n; ++i)
        if(r >= get_distance(arr[i], a, b))
            result[j++] = &arr[i]; //arr + i;
}

int main() {
    Point arr[5] = {0.2, 0.3f, 1.f, 1.5f, 2.f, 2.2f, 0.1f, 0.1f, 3.f, 3.2f};
    int count = count_points_in_circle(arr, 5, 0.f, 0.f, 1.f);
    Point **result = new Point*[count];
    f(arr, 5, 0.f, 0.f, 1.f, result);
    for(int i = 0; i < count; ++i)
        std::cout << "(" << result[i]->x << ":" << result[i]->y << ")\n";
    delete[] result;
    return 0;
}

Zadanie 13*

Radar morski na statku to prawdopodobnie najczęściej używany sprzęt elektroniczny podczas nawigacji. To doskonałe narzędzie do wykrywania obiektów wokół nas. Niestety jesteśmy tak bardzo uzależnieni od radaru, że czasami używamy go częściej niż własnych oczu do obserwacji wokół.

Jednakże inna sytuacja miała miejsce, gdy pewien marynarz odczytywał dane z notatek pozostawionych na pokładzie swojego statku. W notatkach kapitan pokładu zapisał położenia N obiektów na morzu za pomocą współrzędnych biegunowych w następujący sposób:

R1 F1 R2 F2 ... Rn Fn

gdzie R to promień wodzący obiektu (odległość), zaś F to amplituda punktu (wartość kąt skierowanego w radianach). Na kolejnej kartce zapisane było N nazw własnych, które prawdopodobnie dotyczyły zbliżających się okrętów:

Nazwa1 Nazwa2 ... Nazwan

Zaimplementuj funkcję, która posortuje dane przedstawione na obu tych notatkach tak, aby przedstawiła nazwy okrętów w kolejności od najmniej do najbardziej odległego od statku naszego bohatera, który znajduje się w punkcie (0, 0). W sytuacji, gdy oba obiekty są tak samo odległe należy wybrać ten o mniejszej amplitudzie.

Napisz program w języku C++, który przetestuje działanie tej funkcji i wyświetli nazwy zbliżających się okrętów w odpowiedniej kolejności.

Uwagi: R i F mogą być liczbami rzeczywistymi, zaś nazwy okrętów nie są dłuższe niż 99 znakowe słowa. W zadaniu nie można używać struktur i klas.

Przykład:
input:
[1.2 6.1 1.2 1.1 5.4 3.1]
[Alfa Omega Sigma]
output:
Omega Alfa Sigma

Rozwiązanie:
#include <iostream>
#include <algorithm>

bool compare(float r1, float f1, float r2, float f2) {
    if(r1 < r2) return false;
    else if(r1 == r2 && f1 < f2) return false;
    return true;
}

void ships_sort(float cords[], char names[][100], int n) {
    for (int i = 0; i < n - 2; i += 2)
        for (int j = 0; j < n - i - 2; j += 2)
            if (compare(cords[j], cords[j+1], cords[j + 2], cords[j + 3])) {
                std::swap(cords[j], cords[j+2]);
                std::swap(cords[j+1], cords[j+3]);
                std::swap(names[j / 2], names[(j + 2) / 2]);
            }
}

void print_ships_names(char names[][100], int n) {
    for(int i = 0; i < n; ++i) 
        std::cout << names[i] << " "; 
    std::cout << "\n";
}

int main(){
   const int N = 3;
   float ships[2 * N] = {1.2f, 6.1f, 1.2f, 1.1f, 5.4f, 3.1f};
   char ships_names[N][100] = {"Alfa", "Omega", "Sigma"};
   ships_sort(ships, ships_names, 2 * N);
   print_ships_names(ships_names, N);
   return 0;
}

Zadanie 14

Napisz funkcję w języku C++, która otrzymuje jako argumenty tablice points1 i points2 przechowująca elementy typu Point i przepisuje zwartość tablicy points1 do tablicy points2. Zdefiniuj strukturę Point, która ma dwa atrybuty publiczne będące liczbami zmiennoprzecinkowymi.

Rozwiązanie:
#include <iostream>

struct Point {
    float x, y;
};

void rewrite(Point points1[], Point points2[], unsigned int n) {
    for(unsigned int i = 0; i < n; ++i) points2[i] = points1[i];
}

void print_arr(Point arr[], unsigned int n) {
    for(unsigned int i = 0; i < n; ++i)
        std::cout << arr[i].x << " " << arr[i].y << "\n";
    std::cout << std::endl;
}

int main() {
    const unsigned int n = 5;
    Point arr_0[n] = {0.f, 0.f, 1.f, 1.f, 3.f, 3.f, 4.f, 4.f, 7.f, 7.f}, arr_1[n] = {};
    print_arr(arr_1, n);
    rewrite(arr_0, arr_1, n);
    print_arr(arr_1, n);
    return 0;
}

Zadanie 15

Napisz funkcję w języku C++, która otrzymuje jako argumenty tablicę points przechowującej obiekty typu Point (z poprzedniego zadania) i zwraca jako wartość najmniejszą spośród odległości pomiędzy punktami przechowywanymi w tablicy points. Zakładamy, że otrzymana w argumencie tablica ma co najmniej dwa punkty.

Rozwiązanie:
#include <iostream>
#include <cmath>

class Point {
public:
    float x, y;
};

float dist(Point a, Point b) {
    return std::sqrt(std::pow(a.x - b.x, 2) + std::pow(a.y - b.y, 2));
}

float min_distance(Point points[], unsigned int n) {
    float tmp, min = dist(points[0], points[1]);
    for(unsigned int i = 1; i < n - 1; ++i){
        tmp = dist(points[i], points[i + 1]);
        if(tmp < min) min = tmp;
    }
    return min;
}

int main() {
    const unsigned int n = 5;
    Point arr[n] = {0.5f, 0.5f, 1.f, 1.f, 3.f, 3.f, 4.f, 4.f, 7.f, 7.f};
    std::cout << min_distance(arr, n) << std::endl;
    return 0;
}

Zadanie 16

W języku C++ zdefiniuj typ wyliczeniowy, służący do przechowywania informacji o stanie połączenia internetowego, o trzech wartościach odpowiadających trzem stanom: połączenie nawiązane (connected), połączenie nienawiązane (disconnected) i połączenie w trakcie nawiązywania (connecting). Następnie zdefiniuj strukturę komputer przechowującą stan połączenia, IP podłączonego komputera (jako napis) oraz nazwę jego właściciela. Napisz funkcję w języku C++, która jako argument otrzymuje strukturę komputer i wyświetla na standardowym wyjściu jej zawartość w sposób przyjazny dla użytkownika.

Rozwiązanie:
#include <iostream>
#include <cstring>

enum ConnectionStatus {
    connected,
    disconnected,
    connecting
};

struct Computer {
    ConnectionStatus status;
    char ip_address[16];
    char owner_name[100];
};

void print_computer_info(const Computer& computer) {
    std::cout << "Status połączenia: ";
    switch(computer.status) {
        case connected:
            std::cout << "Połączenie nawiązane\n"; break;
        case disconnected:
            std::cout << "Połączenie przerwane\n"; break;
        case connecting:
            std::cout << "Połączenie w trakcie nawiązywania\n"; break;
    }
    std::cout << "IP: " << computer.ip_address << std::endl;
    std::cout << "Właściciel: " << computer.owner_name << std::endl;
}

int main() {
    Computer computer;
    computer.status = connected;
    strcpy(computer.ip_address, "192.168.0.1");
    strcpy(computer.owner_name, "Jan Kowalski");
    
    print_computer_info(computer);
    return 0;
}

Przygotuj się na kolejne laboratorium!

W celu przygotowania się na kolejne zajęcia, spróbuj wykonać poniższe zadania samodzielnie.

Zadanie 1

Zaprojektuj klasę Point posiadającą dwa pola prywatne typu float oraz akcesory i mutatory do tych pól.

Zadanie 2

Do klasy zaprojektowanej w poprzednim zadaniu dopisz metodę move, która przyjmie dwa argumenty typu float: x i y, która przesunie punkt o zadany wektor.

Zadanie 3

Do poprzedniego zadania dopisz funkcję distance, która przyjmie jako argumenty dwa obiekty klasy Point, a następnie zwróci odległość między tymi punktami wyrażoną za pomocą typu float.

Zadanie 4

Usuń z klasy zaprojektowanej w poprzednich zadaniach mutatory. Następnie napisz konstruktor, który przyjmie dwa argumenty typu float i ustawi ich wartości atrybutom x i y. Dodatkowo zaimplementuj metode distance, która przyjmie jako argument obiekt klasy Point, a następnie zwróci odległość między punktem, który wywołuje metodę i punktem przekazanym w argumencie.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *