Soluzioni: Lezione 2

../_images/linea.png

2.1+2.2+2.3 Implementazione di funzionalità aggiuntive nella classe complesso

  • complesso.h

#include <iostream>

#ifndef COMPLESSO_H
#define COMPLESSO_H

class complesso{

public:
    // Constructors
    complesso();
    complesso(double r);
    complesso (double r, double i) ;
    complesso (const complesso & orig);
    // Destructor
    ~complesso () ;

    // Public class methods
    double modulo () ;
    double fase () ;
    void stampami();
    double get_real() { return m_real; };
    double get_imag() { return m_imag; };
    complesso somma(const complesso & orig);
    complesso differenza(const complesso & orig);
    complesso moltiplicazione(const double & fattore);
    complesso moltiplicazione(const complesso & fattore);

    // Operators overloading
    complesso operator+ (const complesso & addendo);  
    complesso &  operator= (const complesso & orig);
    void operator+= (const complesso & addendo);
    void operator+= (const double & addendo);
    void operator-= (const complesso & addendo);
    void operator-= (const double & addendo);
    void operator*= (const complesso & fattore);
    void operator*= (const double & fattore);
    void operator/= (const complesso & fattore);
    void operator/= (const double & fattore);


private:
    // Private members
    double m_real ;
    double m_imag ;

} ;

#endif // COMPLESSO_H
  • complesso.cc

#include "complesso.h"
#include <cmath>


//--------------Constructors------------------

complesso::complesso ():
  m_real (0.),
  m_imag (0.){

    std::cout << "costruzione di un numero complesso" << std::endl ;
}

complesso::complesso (double r):
  m_real (r),
  m_imag (0.){

    std::cout << "costruzione di un numero complesso" << std::endl ;
}

complesso::complesso (double r, double i):
  m_real (r),
  m_imag (i){

    std::cout << "costruzione di un numero complesso" << std::endl ;
}

complesso::complesso (const complesso & orig):
  m_real (orig.m_real),
  m_imag (orig.m_imag){
      //empty
}

//--------------Destructor------------------

complesso::~complesso (){
    // ~ deletes the "complesso" object and stack allocated memory.
    // This is the place to clean the dinamically allocated memory.
    // In this example we do not have dinamically allocated memory so the destructor is empty.

    std::cout << "distruzione di un numero complesso" << std::endl;
}

//--------------Public methods---------------

double complesso::modulo (){

    return sqrt(m_real * m_real + m_imag * m_imag) ;

}

double complesso::fase(){

    double mod = modulo();

    if (mod == 0){
        throw "Parte reale e immaginaria = 0. La fase non è definita";
    }

    return acos(m_real/mod);
}


void complesso::stampami (){


    std::cout << this->m_real << " + " << this->m_imag << "i" << std::endl ;
    return ;

}


complesso complesso::somma(const complesso & orig){
  complesso ret(m_real + orig.m_real, m_imag + orig.m_imag);
  return ret;
}


complesso complesso::differenza(const complesso & orig){
  complesso ret(m_real - orig.m_real, m_imag - orig.m_imag);
  return ret;
}

complesso complesso::moltiplicazione(const complesso & fattore){
  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  double new_m_real = (m_real_copy*fattore.m_real) - (m_imag_copy*fattore.m_imag) ;
  double new_m_imag = (m_real_copy*fattore.m_imag) + (m_imag_copy*fattore.m_real) ;

  complesso ret(new_m_real, new_m_imag);
  return ret;
}

complesso complesso::moltiplicazione(const double & fattore){
  complesso ret(fattore*m_real, fattore*m_imag);
  return ret;
}


//--------------Overloading---------------

complesso
complesso::operator+ (const complesso & addendo)
{
 complesso somma (m_real, m_imag) ;
 somma.m_real = somma.m_real + addendo.m_real ;
 somma.m_imag = somma.m_imag + addendo.m_imag ;
 return somma ;
}

complesso &  complesso::operator= (const complesso & orig){
  m_real = orig.m_real ;
  m_imag = orig.m_imag ;
  return *this ;
}

void complesso::operator+= (const complesso & addendo){
  m_real = m_real + addendo.m_real ;
  m_imag = m_imag + addendo.m_imag ;
  return ;
}

void complesso::operator+= (const double & addendo){
  m_real = m_real + addendo;
  return ;
}

void complesso::operator-= (const complesso & addendo){
  m_real = m_real - addendo.m_real ;
  m_imag = m_imag - addendo.m_imag ;
  return ;
}

void complesso::operator-= (const double & addendo){
  m_real = m_real - addendo;
  return ;
}

void complesso::operator*= (const complesso & fattore){
  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  m_real = (m_real_copy*fattore.m_real) - (m_imag_copy*fattore.m_imag) ;
  m_imag = (m_real_copy*fattore.m_imag) + (m_imag_copy*fattore.m_real) ;
  return ;
}

void complesso::operator*= (const double & fattore){
  m_real = m_real*fattore ;
  m_imag = m_imag*fattore ;
  return ;
}

void complesso::operator/= (const complesso & fattore){

  double denom = (fattore.m_real*fattore.m_real) + (fattore.m_imag*fattore.m_imag); // c^2 + d^2

  //Controlli
  if (denom==0){
    if (fattore.m_real){
      m_real = m_real/fattore.m_real;
      return;
    }
    else{
      std::cout << "Divisione per 0, ricontrolla inputs" << std::endl;
      return;
    }
  }

  const double m_real_copy(m_real), m_imag_copy(m_imag); //definisco copie da usare nelle operazioni
  m_real = (m_real_copy*fattore.m_real + m_imag_copy*fattore.m_imag)/denom ;
  m_imag = (m_imag_copy*fattore.m_real - m_real_copy*fattore.m_imag)/denom ;
  return ;
}

void complesso::operator/= (const double & fattore){

  if(fattore == 0){
    std::cout << "Errore: Divisione per 0" << std::endl;
    return;
  }

  m_real = m_real/fattore ;
  m_imag = m_imag/fattore ;
  return ;
}
  • main.cpp

//c++ -o main main.cpp complesso.cc

#include "complesso.h"

int main(){

    //-----------  Testing constructors  -----------

    complesso c_num1; //default
    c_num1.stampami();

    complesso c_num2(1); //real
    c_num2.stampami();

    complesso c_num3(3,5); //real + i*imag
    c_num3.stampami();

    complesso c_num_copy(c_num2); //copying, only real
    c_num_copy.stampami();

    //-----------  Testing destructor in a new scope ({})  -----------

    {
        complesso in_new_scope;
        
    } // here "in_new_scope" goes out of scope and destructor is called.

    //-----------  Testing class methods  -----------

    std::cout << "Il modulo di 3 +i5 è: " << c_num3.modulo() << " la fase è: " << c_num3.fase() << std::endl;


    // Trying to compute the phase of 0 + i0 (0/0). Catching the exception to continue
    try {
      double fase_impossibile = c_num1.fase(); 
    } catch (const char* msg) {
     std::cerr << msg << std::endl;
    }

    //-----------  Testing overloading =, +, *  -----------

    std::cout << "Test overloading operatori" << std::endl;
    c_num1 = c_num3;    // = overloading
    c_num1.stampami();

    c_num1 += 1;        // += numero reale
    c_num1.stampami();
    c_num1 += c_num3;   // += numero complesso
    c_num1.stampami();
    c_num1 -= 1;        // -= numero reale
    c_num1.stampami();
    c_num1 -= c_num3;   // -= numero complesso
    c_num1.stampami();

    c_num1 *= 2;        // *= numero reale
    c_num1.stampami();
    c_num1 *= c_num3;   // *= numero complesso
    c_num1.stampami();
    c_num1 /= 2;        // /= numero reale
    c_num1.stampami();
    c_num1 /= c_num3;   // /= numero complesso
    c_num1.stampami();

    //----------- End -----------

    std::cout << "Al termine dell'esecuzione della funzione main" \
                    " verrà distrutto tutto ciò creato al suo interno: " << std::endl;

    return 0;
}
../_images/linea.png

2.4 Classe mioArray

  • myarray.h

#ifndef MY_ARRAY_H
#define MY_ARRAY_H

#include <iostream> 

class mioArray{

    public:

        //Il costruttore prende in input la dimensione e inizializza il membro ( dim(d) ).
        //Successivamente crea dinamicamente l'array mioarr(). Nell'espressione logica tra parentesi
        //si chiede (simbolo ?) se dim > 0 (interi diversi da 0 sono true). Se è vero allora viene
        //inizializzato l'array (new double[dim]) altrimenti (:) viene inizializzato l'oggetto a nullptr.
        mioArray(int d): dim(d), mioarr(dim ? new double[dim] : nullptr) {}; 
        mioArray(const mioArray& copy);
        ~mioArray(){ delete[] mioarr; };

        bool check_idx(int idx) const;
        double get1(int idx) const;
        double get2(int idx) const;
        void fill(int idx, double num);
        void print() const; //opzionale, solo per comodità

    private:
        int dim;
        double* mioarr;


};

#endif //MY_ARRAY_H
  • myarray.cc

#include "myarray.h"

mioArray::mioArray(const mioArray& copy)
    : mioarr(copy.dim ? new double[copy.dim] : nullptr)
    , dim(copy.dim){
        /*
            Copy constructor.
            Viene copiato il contenuto solo se mioarr non punta a null.
        */
        if (mioarr){
            for(int i = 0; i < dim; i++){
                mioarr[i] = copy.mioarr[i];
            }
        }
}

bool mioArray::check_idx(int idx) const{
    /*
        Semplice check tra l'indice (idx) inserito e la dimensione
        dell'array (attributo della classe).
    */
    return idx < dim;
}

double mioArray::get1(int idx) const{
    /*
        Prima funzione per richiedere un valore dall'array. 
        Viene svolto un check sull'indice e, se più grande della dimensione
        dell'array, viene stampato un messaggio a schermo.
        In ogni caso viene restituito il valore nella cella di memoria
        a cui punta mioarr[idx] = *(mioarr + idx)...Qualunque essa sia!

        -->Questo vuol dire che se, per sbaglio, sbagliamo l'indexing dell'array
        C++ non ci avvertirà ma restituirà un valore "a caso". C++
        non implementa check sui limiti degli oggetti (per mantenere alte performances).
    */
    if (!check_idx(idx)){
        std::cout << "L'indice inserito è più grande della dimensione dell'array" << std::endl;
    }      
    return mioarr[idx]; //questo non da mai errore
}

double mioArray::get2(int idx) const{
    /*
        Seconda funzione per richiedere un valore dall'array. 
        Viene svolto un check sull'indice e, se più grande della dimensione
        dell'array, viene lanciato un errore del tipo out_of_range con un messaggio a 
        piacere all'interno delle parentesi (). Se la richiesta è valida viene restituito il valore.
    */
    if (!check_idx(idx)){
        throw std::out_of_range ("L'indice inserito è più grande della dimensione dell'array\n");
    }      

    return mioarr[idx];
}



void mioArray::fill(int idx, double num){
    /*
        Metodo per riempire l'array. Richiesto in input l'indice (idx) e il valore
        da assegnare alla cella di memoria puntata da (mioarr+idx) -> mioarr[idx] = num.
        Se l'indice supera la dimensione dell'array viene visualizzato a terminale un messaggio 
        e non viene riempita la cella di memoria. 
        L'operazione mioarr[idx] = num funzionerebbe anche se idx > dim.
    */

    if (check_idx(idx)){
        mioarr[idx] = num;
        return;
    }
    else{
        std::cout << "L'indice inserito è più grande della dimensione dell'array" << std::endl;
        return;
    }

}

void mioArray::print() const{

    std::cout << "Mio array:" << std::endl;
    for(int i = 0; i < dim; i++){
        std::cout << mioarr[i] << " ";
    }
    std::cout << std::endl;

    return;
}
  • main.cpp

// c++ -o main main.cpp myarray.cc

#include "myarray.h"

int main(){

    int dim = 10;

    mioArray arr(dim);

    //Riempio l'array con dummy values
    for(int i = 0; i < dim; i++){
        arr.fill(i, i);
    } 

    //Mostra contenuto
    arr.print();

    //modifico valore di una cella
    arr.fill(1, 10.5);

    //Mostra contenuto ma usando la funzione get
    std::cout << "Mio array: " << std::endl;
    for(int i = 0; i < dim; i++){
        std::cout << arr.get1(i) << " ";
    } 
    std::cout << std::endl;

    //Proviamo a chiedere il valore dell'array per un indice > dimensione
    double num = arr.get1(11);
    std::cout << "La prima funzione get1 restituisce un valore " << num << " anche se l'indice richiesto 11 "\
                    "supera la dimensione dell\'array " << dim << std::endl;

    //Proviamo a fare lo stesso con la seconda funzione. 
    //La sintassi try-catch viene usata per continuare nell'esecuzione del programma.
    //Senza di essa il programma si bloccherebbe con messaggio di errore come output da terminale (e.what()).
    try {
        double error = arr.get2(11);
    }
    catch (const std::out_of_range& e) {
        std::cout << "La seconda funzione restituisce un errore se l'indice richiesto supera "\
                        "la dimensione dell\'array.\nErrore: " << e.what();
    }
    

    //Test con un oggetto di tipo const
    //Copiamo il contenuto di arr in un nuovo oggetto const new_arr
    const mioArray new_arr(arr);

    //stampiamo
    new_arr.print();

    std::cout << "Possiamo ricevere informazioni dall'oggetto di tipo const ad esempio " << new_arr.get2(1) << std::endl;

    return 0;

}