Polinom — nađi grešku

Klasa Polinom čuva koeficijente polinoma $a_0 + a_1 x + \dots + a_n x^n$ u dinamički alociranom nizu. Program se prevodi bez upozorenja, ali ne radi ispravno.

  1. Prevesti i pokrenuti program bez sanitajzera. Šta se ispisuje, a šta je očekivano?
  2. Prevesti sa sanitajzerom i pročitati izveštaj: tip greške, gde je memorija pročitana i gde je oslobođena.
  3. Objasniti šta se dešava pri pozivu izvod(p) i pri dodeli q = p.
  4. Ispraviti klasu tako da program ispiše očekivane vrednosti.
g++ -std=c++17 -Wall -Wextra -g main.cpp -o program
g++ -std=c++17 -Wall -Wextra -g -fsanitize=address,undefined main.cpp -o program

Pitanje za razmišljanje: Program počinje da radi i ako se parametar funkcije izvod promeni u const Polinom&. Da li je greška u funkciji izvod ili u klasi? Šta je sa dodelom q = p?

Ko nema sanitajzer: postaviti prekidnu tačku u destruktor i pratiti vrednost pokazivača koef — ista adresa se oslobađa dva puta.

Dodatno: Napisati klasu Polinom tako da koeficijente čuva u nizu tipa DinamickiNiz<int>. Da li su tada potrebni destruktor, konstruktor kopije i operator dodele?

Ulaz

Program nema ulaz.

Izlaz

p(2) = 29
d(2) = 38
p(0) = 1

Postavka

main.cpp

// POCETNI KOD ("nadji gresku").
// Program se prevodi bez upozorenja, ali ne radi ispravno. Zasto?
//
// g++ -std=c++17 -Wall -Wextra -g main.cpp -o program && ./program
// g++ -std=c++17 -g -fsanitize=address main.cpp -o program && ./program

#include <cstddef>
#include <iostream>

// Polinom a0 + a1 x + ... + an x^n; koeficijenti u dinamicki alociranom nizu.
class Polinom {
private:
    int* koef;
    std::size_t stepen_;

public:
    explicit Polinom(std::size_t stepen)
        : koef(new int[stepen + 1]), stepen_(stepen)
    {
        for (std::size_t i = 0; i <= stepen_; ++i)
            koef[i] = 0;
    }

    ~Polinom()
    {
        delete[] koef;
    }

    std::size_t stepen() const { return stepen_; }
    int& operator[](std::size_t i) { return koef[i]; }
    int operator[](std::size_t i) const { return koef[i]; }

    long long vrednost(long long x) const
    {
        long long rez = 0;                       // Hornerova sema
        for (std::size_t i = stepen_ + 1; i > 0; --i)
            rez = rez * x + koef[i - 1];
        return rez;
    }
};

Polinom izvod(Polinom p)
{
    Polinom d(p.stepen() > 0 ? p.stepen() - 1 : 0);
    for (std::size_t i = 1; i <= p.stepen(); ++i)
        d[i - 1] = static_cast<int>(i) * p[i];
    return d;
}

int main()
{
    Polinom p(3);                // p(x) = 1 + 2x + 3x^3
    p[0] = 1;
    p[1] = 2;
    p[3] = 3;

    Polinom d = izvod(p);        // d(x) = 2 + 9x^2
    std::cout << "p(2) = " << p.vrednost(2) << std::endl;   // ocekivano 29
    std::cout << "d(2) = " << d.vrednost(2) << std::endl;   // ocekivano 38

    Polinom q(1);
    q = p;
    q[0] = 100;
    std::cout << "p(0) = " << p.vrednost(0) << std::endl;   // ocekivano 1

    return 0;
}

Rešenje

main.cpp

#include <cstddef>
#include <iostream>

// Polinom a0 + a1 x + ... + an x^n; koeficijenti u dinamicki alociranom nizu.
class Polinom {
private:
    int* koef;
    std::size_t stepen_;

public:
    explicit Polinom(std::size_t stepen)
        : koef(new int[stepen + 1]), stepen_(stepen)
    {
        for (std::size_t i = 0; i <= stepen_; ++i)
            koef[i] = 0;
    }

    ~Polinom()
    {
        delete[] koef;
    }

    // Podrazumevani konstruktor kopije i operator dodele kopiraju samo
    // pokazivac koef, pa bi dva objekta delila (i dva puta oslobodila) isti
    // blok. Klasa sa destruktorom koji oslobadja memoriju mora da definise
    // i kopiranje (pravilo trojke).
    Polinom(const Polinom& drugi)
        : koef(new int[drugi.stepen_ + 1]), stepen_(drugi.stepen_)
    {
        for (std::size_t i = 0; i <= stepen_; ++i)
            koef[i] = drugi.koef[i];
    }

    Polinom& operator=(const Polinom& drugi)
    {
        if (this == &drugi)
            return *this;

        // Novi blok se obezbedjuje i popunjava pre oslobadjanja starog.
        int* novi = new int[drugi.stepen_ + 1];
        for (std::size_t i = 0; i <= drugi.stepen_; ++i)
            novi[i] = drugi.koef[i];

        delete[] koef;
        koef = novi;
        stepen_ = drugi.stepen_;
        return *this;
    }

    std::size_t stepen() const { return stepen_; }
    int& operator[](std::size_t i) { return koef[i]; }
    int operator[](std::size_t i) const { return koef[i]; }

    long long vrednost(long long x) const
    {
        long long rez = 0;                       // Hornerova sema
        for (std::size_t i = stepen_ + 1; i > 0; --i)
            rez = rez * x + koef[i - 1];
        return rez;
    }
};

Polinom izvod(Polinom p)
{
    Polinom d(p.stepen() > 0 ? p.stepen() - 1 : 0);
    for (std::size_t i = 1; i <= p.stepen(); ++i)
        d[i - 1] = static_cast<int>(i) * p[i];
    return d;
}

int main()
{
    Polinom p(3);                // p(x) = 1 + 2x + 3x^3
    p[0] = 1;
    p[1] = 2;
    p[3] = 3;

    Polinom d = izvod(p);        // d(x) = 2 + 9x^2
    std::cout << "p(2) = " << p.vrednost(2) << std::endl;   // ocekivano 29
    std::cout << "d(2) = " << d.vrednost(2) << std::endl;   // ocekivano 38

    Polinom q(1);
    q = p;
    q[0] = 100;
    std::cout << "p(0) = " << p.vrednost(0) << std::endl;   // ocekivano 1

    return 0;
}