Classi Composte in C++

Le classi composte sono comprese tra le possibilità messe a disposizione dal linguaggio di programmazione C++ che è noto per il suo supporto all’orientamento agli oggetti. Tra i concetti fondamentali dell’orientamento agli oggetti in C++, le classi composte giocano un ruolo cruciale nell’incapsulare dati e funzionalità in strutture più complesse.

Cos’è una Classe Composta?

Una classe composta in C++ è una classe che contiene uno o più oggetti di altre classi come membri. Questo concetto è noto anche come “composizione” e consente agli sviluppatori di costruire strutture più complesse combinando più classi in una singola entità.

Vantaggi della Composizione

  1. Incapsulamento Avanzato: Le classi composte supportano un livello più avanzato di incapsulamento. Ogni oggetto contenuto all’interno di una classe composta può essere accessibile solo tramite l’interfaccia pubblica della classe contenitore, contribuendo a mantenere l’integrità dei dati e a limitare l’accesso non autorizzato.
  2. Riutilizzo del Codice: La composizione promuove il riutilizzo del codice. Una classe composta può riutilizzare funzionalità esistenti fornite da altre classi senza la necessità di ereditarietà, semplificando la gestione delle dipendenze.
  3. Struttura Gerarchica: La composizione consente di costruire strutture gerarchiche complesse. Ad esempio, un oggetto di una classe composta può contenere oggetti di altre classi composte, creando una gerarchia di oggetti anche complessa ma ben organizzata.

Particolarità

La cosa più particolare è che il costruttore di una classe composta deve, prima di fare altro, richiamare implicitamente o esplicitamente il costruttore dell’attributo che è oggetto di un’altra classe, si rimanda alla pagina specifica sui costruttori delle classi composte.

Ulteriore particolarità da sottolineare è che in una classe posso usare direttamente i propri membri (metodi e attributi) sia pubblici che privati, ma se un attributo è un oggetto dalla classe che lo incorpora di quell’oggetto si potranno usare direttamente solo i membri (metodi o attributi) pubblici.

Esempio

Ecco un esempio di una classe composta in C++, la classe rappresenta una coppia di persone di cui vegono memorizzata altezza ed età e il loro numero di figli.

#include <iostream>

using namespace std;

class Persona
{

    int eta;
    double altezza;

public:
    // Costruttore
    Persona(int e, double a) : eta(e), altezza(a) {}

    // Metodo set per l'età
    void setEta(int e)
    {
        eta = e;
    }

    // Metodo set per l'altezza
    void setAltezza(double a)
    {
        altezza = a;
    }

    // Metodo get per l'età
    int getEta()
    {
        return eta;
    }

    // Metodo get per l'altezza
    double getAltezza()
    {
        return altezza;
    }
};

class Coppia
{
    Persona p1;
    Persona p2;
    int numeroFigli;
    public:
    Coppia(int e1, double a1, int e2, double a2, int nf):p1(e1,a1),p2(e2,a2)
    {
         numeroFigli=nf;
    }
    // Metodo get per il numero di figli
    int getNumeroFigli()
    {
        return numeroFigli;
    }
    // Metodo aumentare di uno il numero di figli
    void nuovoFiglio()
    {
        numeroFigli++;
    }
    // Metodo per restituire l'altezza media della coppia
    double altezzaMedia()
    {
        return (p1.getAltezza()+p2.getAltezza())/2;
    }
    // Metodo per restituire l'altezza della prima persona della coppia
    double getP1Altezza()
    {
        return p1.getAltezza();
    }
    // Metodo per restituire l'altezza della seconda persona della coppia
    double getP2Altezza()
    {
        return p2.getAltezza();
    }
     // Metodo per restituire l'età della prima persona della coppia
    double getEtaPersona1()
    {
        return p1.getEta();
    }
    // Metodo per restituire l'età della seconda persona della coppia
    double getEtaPersona2()
    {
        return p2.getAltezza();
    }
    //Metodo compleanno persona 1
    void compleannoP1()
    {
        p1.setEta(p1.getEta()+1);
    }
    //Metodo compleanno persona 2
    void compleannoP2()
    {
        p2.setEta(p2.getEta()+1);
    }
};

int main() {
    // Creazione di un oggetto Coppia
    Coppia coppia1(25, 1.75, 28, 1.90, 1);

    // Utilizzo del metodi get per ottenere i valori
    cout<<"numero figli "<<coppia1.getNumeroFigli()<<endl;
    cout<<"eta' persona 1: " << coppia1.getEtaPersona1() << " anni" << endl;
    
    // Uso i metodi per aumentare l'eta' della prima persona della coppia
    coppia1.compleannoP1();
    
    // Stampa dei nuovi valori
    cout << "Nuova età: " << coppia1.getEtaPersona1() << " anni" << endl;

    return 0;
}

Codice eseguibile in C++

In questo esempio, la classe Coppia ha tre attributi privati p1 e p2 che sono le due persone che formano la coppia e il loro numero di figli. La classe coppia fornisce metodi pubblici per lavorare con i metodi delle due persone e i loro attributi. Il costruttore inizializza gli attributi al momento della creazione dell’oggetto, richiamando i costruttori di persona. I metodi getEta consentono di recuperare i valori degli attributi che rappresentano le età delle persone.

Esercizi

Per esercizi sulle classi composte si rimanda all’apposita sezione della pagina con gli esercizi sulla programmazione ad oggetti.

Conclusioni

Le classi composte in C++ offrono una potente via per organizzare e strutturare il codice in modo modulare. Consentono la creazione di strutture complesse attraverso la combinazione di più classi, promuovendo l’incapsulamento e il riutilizzo del codice. Comprendere come utilizzare la composizione è fondamentale per sviluppare software robusto, manutenibile ed efficiente in C++.

Come si fa la radice quadrata in C++

In questo articolo vedremo come calcolare la radice quadrata in C++ con la funzione predefinita e già pronta nelle librerie di C++.

Per poter calcolare la radice quadrata è necessario importare la libreria delle funzioni matematiche cmath, per importare la liberria è necessario inserire all’inizio del programma l’istruzione

#include<cmath>

Una volta importata la libreria si può usare tranquillamente la funzione sqrt per calcolare la radice quadrata, la funzione sqrt prende come parametro un numero con la virgola e ne restituisce un altro con la virgola.

Il seguente programma di esempio fa inserire all’utente un numero con la virgola e ne calcola e restituisce la radice quadrata.

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
   float n;
   cin>>n;
   float r=sqrt(n);
   cout<<r;
}

Prova il programma per calcolare la radice quadrata di un numero scritto qui sopra.

Curiosità: Perchè la funzione per calcolare la radice quadrata si chiama sqrt?

Si chiama così perché in inglese radice quadrata si dice square root, square vuol dire quadrato e root vuol dire radice, tenendo solo le consonanti viene fuori effettivamente sqrt, sapendolo si può anche ricordare più facilmente il nome di questa funzione.

Funzioni in C++, spiegazione, sintassi ed esempi

Le funzioni in C++ , e in generale in tutti i linguaggi di programmazione, sono sostanzialmente dei blocchi di codice riutilizzabili; possono essere pensate come dei “sottoprogrammi” richiamabili da altri programmi (o altri “sottoprogrammi”/funzioni).

Lo scopo principale dell’uso delle funzioni è quello di poter riutilizzare il codice che si scrive (senza doverlo quindi riscrivere più volte!), inoltre l’utilizzo delle funzioni aumenta la leggibilità dei programmi che le richiamano snellendone il codice e aiuta a dividere la risoluzione di un problema in sottoproblemi.

Le funzioni si comportano a tutti gli effetti come dei programmi veri e propri con la differenza che possono interagire comunicando col programma chiamante (potrebbe essere il main o un’altra funzione).
La comunicazione di una funzione col programma chiamante avviene generalmente tramite quelli che vengono chiamati parametri, questi permettono alla funzione di ricevere e/o restituire valori, oltre ai parametri in verità c’è anche un altro modo per attuare questa comunicazione che vedremo in seguito.

Esempio funzione in C++

Il programma dell’esempio qui riceve un numero intero dall’utente e ne scrive il quadrato dopo averlo calcolato grazie a una funzione, in grassetto la funzione e l’istruzione del main in cui viene richiamata la funzione.

#include<iostream>
using namespace std;
int funzione (int paramentro)
{
   int q=parametro*parametro; 
   return q;
}
int main()
{
   int n;
   cin>>n;
   int quadrato=funzione(n);
   cout<<quadrato;
}

codice eseguibile in C++ dell’esempio sulla funzione

Sintassi funzioni in C++

In generale le funzioni si scrivono così:

tipo nomeFunzione (tipoP1  parametro1, tipoP2 parametro2)
{
   //codice della funzione
   return valore; //il return c'è se solo se la funzione restituisce qualcosa
}

nel precedente:
– nomeFunzione rappresenta il nome che identificherà la specifica funzione;
– al posto di tipo va inserito il tipo di valore (int, float, double, bool, char) che verrà restituito al programma chiamante dalla funzione, se la funzione non deve restituire nulla si dovra inserire void;
– tra parentesi tonde vanno inseriti i parametri che riceverà la funzione e i loro tipi, si veda l’approfondimento sotto;
– tra parentesi grafe va il copro del codice della funzione ovvero il codice vero e proprio della funzione;
– se la funzione deve restituire qualcosa al programma chiamante (ovvero all’inizio non c’è void) questa restituzione deve avvenire tramite l’istruzione return seguita dal valore o dalla variabile di cui restituire il contenuto al programma chiamante.

I parametri possono essere ricevuti dalla funzione in due modi o per valore o per riferimento.

funzioni con paramentri per valore

Parametri per valore: i parametri passati per valore vanno specificati all’interno delle parentesi tonde dopo il nome della funzione specificando per ogni parametro il nome preceduto dalla sua tipologia; quando sono presenti più parametri vanno separati da virgolo. Quando si passa un parametro in questo modo all’inizio dell’esecuzione della funzione il valore memorizzato all’interno della variabile passata come parametro viene copiato in una nuova variabile della funzione.
I parametri passati per valore possono essere anche opzionali, ovvero si possono creare funzioni che possono ricevere un numero diverso di parametri; questo si fa assegnando ad alcuni parametri dei valori di default.

Funzioni con parametri per riferimento

Parametri per riferimento: affinché una funzione riceva un parametro in questo modo è necessario anteporre una & prima del nome del parametro. I parametri passati in questo modo fanno sì che i cambiamenti che all’interno del codice della funzione su un parametro avvengano anche sulle variabili del programma chiamante.
I parametri passati per riferimento non possono né essere opzionali né essere valori numerici.
Ad esempio

void funzione (int valore,int &riferimento)
{
   valore++;
   riferimento++;
}
int main()
{
   int x=0;
   int y=0;
   funzione(x,y);
   cout<<x<<endl; //stamperà 0 poiché x è passato per valore;
   cout<<y<<endl; //stamperà 1 poiché y è passato per riferimento;
}

codice eseguibile dell’esempio funzione con parametri per riferimento in C++

Parametri di ritorno: ogni funzione può essere impostata o meno restituire un valore al programma che la chiama. La tipologia del valore deve essere dichiarata prima del nome della funzione; all’interno della funzione la restituzione di un valore al programma chiamante avviene tramite il comando return seguito dal valore o dalla variabile il cui contenuto deve essere restituito. Qualora una funzione non restituisca nulla il nome della funzione deve essere preceduto dalla parola void; talvolta le funzioni che non restituiscono nulla vengono chiamate “procedure”.

Variabili globali: le funzioni non possono operare direttamente sulle variabili dei programmi chiamanti, che appartengono ai rispettivi spazi delle variabili, tuttavia si possono dichiarare delle variabili globali che, dichiarate fuori da qualsivoglia parentesi grafa prima del codice del main, possono essere usate sia dal main che da tutte le altre funzioni.

OVERLOADING DELLE FUNZIONI

Due funzioni possono avere lo stesso nome solo se hanno una diverso numero di parametri, parametri di diversa tipologia o un diverso ordine dei parametri.
Non si possono avere due funzioni con lo stesso nome e con lo stesso numero, tipologia e ordine dei parametri perché questo creerebbe un’ambiguità non risolvibile nell’interpretazione delle istruzioni, non essendoci modo per capire quale fra le due funzioni dovrebbe essere chiamata.

APPROFONDIMENTO – FUNZIONI E ARRAY

Vai all’articolo apposito funzioni e array in C++ per sapere come si comportano le funzioni quando ricevono array come parametri in C++.

ESERCIZI SULLE FUNZIONI

Per esercitarti con le funzioni vai all’articolo apposito: esercizi con le funzioni in C++.

Funzioni e array

ARRAY COME PARAMETRI
In C++ le funzioni possono tranquillamente lavorare con parametri che sono array, tuttavia gli array passati come parametri vengono sempre passati per riferimento (senza dover inserire la “&” prima del nome dell’array).

RETURN E ARRAY
In C++ le funzioni non possono restituire direttamente array tramite il return.
Un metodo semplice per aggirare il problema di non poter restituire array è quello di passare appositamente l’array di destinazione tra i parametri e lavorare direttamente su quello.

Costruttore copia in C++

Il costruttore copia è un costruttore di una classe che ha un solo parametro che è un oggetto dello stesso tipo del nuovo oggetto che si vuole creare e serve appunto per dichiarare un oggetto ed inizializzarlo a partire da un oggetto esistente dello stesso tipo.

In C++ ogni classe di default possiede un costruttore copia, che esegue la copia membro a membro dell’oggetto passato come parametro nel nuovo oggetto; tutti i valori degli attributi del vecchio oggetto verranno copiati in quelli del nuovo oggetto (oggetti compresi).

// si crea l'istanza copia di tipo A
// copiando l'istanza originale (che deve essere di tipo A)
A copia(originale)

In generale a meno di esigenze particolari (come nel caso in cui ci siano attributi che siano puntatori, ma non lo tratteremo in questa sede) non serve quindi generalmente scriverlo esplicitamente all’interno della classe essendo già implicitamente presente.
Quando si definisce un costruttore copia, quello di default cessa ovviamente di esistere.

Qualora si voglia far sì che il costruttore copia non possa essere richiamato dall’esterno della classe, lo si può definire esplicitamente rendendolo privato.

Per la ridefinizione del costruttore copia si deve passare l’oggetto parametro per riferimento (per non crearne una copia nel passaggio) dichiarandolo anche const per questioni di sicurezza (per evitare modifiche).

Esempio: Viene creato un costruttore copia per la classe A e poi dopo aver creato un oggetto viene copiato con il costruttore copia.

class A
{
int x,y;
public:
A(int px,int py)
{
x=px;
y=py
}
A(const A& p)
{
x=p.x;
y=p.y;
}

/* eventuali altri metodi */
}

int main()
{

int n1,n2;
cin>>n1>>n2;
A originale(n1,n2);
A copia(originale);
}

RICHIAMI IMPLICITI AL COSTRUTTORE COPIA

Bisogna porre attenzione prima di modificare a cuor leggero il costruttore copia, perché questo viene invocato implicitamente nei seguenti casi:

INIZIALIZZAZIONE NUOVO OGGETTO DA UNO ESISTENTE

Il primo caso in cui viene chiamato implicitamente il costruttore copia è quando si crea un nuovo oggetto e lo inizializza tramite un oggetto esistente dello stesso tipo.
A istanzaNuova = istanza;

Passaggio per valore di un oggetto come parametro a una funzione

Il secondo caso in cui viene chiamato implicitamente il costruttore copia è quando viene passata un’istanza a una funzione (o a un metodo) come parametro per valore, l’istanza originale viene copiata nel parametro.

int funzione (A istanza)

Restituzione (return) di un’istanza passata come parametro per riferimento a una funzione

Il terzo caso in cui viene chiamato implicitamente il costruttore copia è quando in una funzione o in un metodo si restituisce tramitre il return al programma chiamante un’istanza che le era stata passata come parametro per riferimento, la funzione (o il metodo) restituisce una copia dell’istanza originale:

A funzione(A &p)
{
return p;
}

int main()
{
A oggetto(2,3);
cout<<funzione(oggetto).getX();
}

codice C++ sul costruttore copia realizzato nel video.

Costruttori in C++ nelle classi (programmazione ad oggetti)

I costruttori in C++ servono nella programmazione ad oggetti a creare istanze di una classe e ad inizializzarne i valori degli attributi.
I costruttori sono metodi speciali, caratterizzati dal fatto di avere lo stesso nome della classe e dal fatto che creando un nuovo oggetto non occorre dire cosa restituiscono, perché servono appunto a creare un’istanza della classe in cui sono definiti.

Esempio:

Class Rettangolo
{
    int base;
    int altezza;
public:
    Rettangolo (int b, int h)
   { 
               base=b;
               altezza=h;
   }
}

Utilizzo:

int main()
{
   Rettangolo r(12,42);
}

Costruttore di default
Se e solo se in una classe non è esplicitato nessun costruttore, è implicitamente e automaticamente definito un costruttore di default senza parametri, che permette di creare oggetti di quella classe per poi poterci lavorare.

Nota Bene: bisogna porre attenzione in questi casi perché alla creazione di un’istanza di quella classe, non sarà noto il contenuto dei suoi attributi.

Class Rettangolo
{
int base;
int altezza;
}

Utilizzo:

int main()
{
Rettangolo r;
}

Costruttore copia di default
In ogni classe c’è il costruttore copia ed è implementato automaticamente (a meno che non sia sovrascritto),; il costruttore copia di default opera copiando i valori di tutti gli attributi di un oggetto esistente (passato come parametro al costruttore) nel nuovo oggetto creato dal costruttore.

Rettangolo r2 (r1);

Nota: volendo si può definire manualmente il costruttore copia di una classe di fatto sovrascrivendo quello, per approfondire vai alla pagina costruttore copia.

Richiamare da un costruttore un altro costruttore
Quando si definisce un costruttore si può fare in modo che il costruttore richiami un altro costruttore della stessa calsse già dichiarato.

Ad esempio:

Rettanagolo (float  x):Rettangolo (x,x)
{}

In questo esempio il costruttore di Rettangolo con un solo parametro richiama il costruttore di Rettangolo con due parametri, passando due volte lo stesso valore costruendo così un quadrato.

Scrittura compatta per assegnare valori agli attributi tramite il costruttore
Dopo le parentesi tonde dei paramentri che vengono passati al costruttore si possono inizializzare gli attributi, scrivendo dopo i due punti i nomi degli attributi con tra parentesi tonde il valore da assegnargli, nel caso si vogliano inizializzare più attributi in questo modo, vanno separati da virgole-
Ad esempio:

Class Rettangolo 
{     
     int base;
     int altezza; 
     public:     
     Rettangolo (int b, int h):base(b),altezza(h)    
     {}
}

Costruttore nelle classi composte
Vai alla pagina sul costruttore nelle classi composte per capire come impostare un costruttore in modo che richiami il costruttore dei suoi attributi.

Precedenza e ordine di esecuzione dei principali operatori in C++ come and e or

Spesso capita di chiedersi in quale ordine vengano eseguite le operazioni in C++, ovvero quale sia la precedenza tra gli operatori operatori predefiniti.

Qui sotto si trova una tabella con alcuni dei principali operatori in ordine decrescente di precedenza in C++ (più sono in alto nella tabella maggiore precedenza hanno), vuol dire che se in un’istruzione compaiono più operatori, verrò svolto prima quello che compare più in alto in questa tabella.

Operatori nella stessa riga della tabella hanno la stessa precedenza.

Tabella delle precedenze dei principali operatori in C++

Operatore Descrizione
:: Risolutore di  ambito (scope)
++ -- Suffisso di incremento e decremento
() Chiamata a una funzione
[] Individuazione di una cella di un Array
. Selezione di un elemento per referenza
++ --
!
(type)
prefisso di incremento e decremento
NOT logico
Cast a un Type
* / % Moltiplicazione, divisione, resto della divisione intera
+ Addizione e sottrazione
< <=
> >=
Operatori relazionali “<” minore e “≤” minore o uguale
Operatori relazionali “>” maggiore e “≥” maggiore o uguale
== != Operatori relazionali di uguaglianza =
o disuguaglianza ≠
&& AND logico
|| OR logico
=
+=
−=
*= /= %=
Assegnamento diretto
Assegnamento per somma e differenza
Assegnamento per prodotto, quoziente e resto

Esempio precedenza operatori

cout << 1 + 5 * 2 ;  
// scriverà 11 eseguendo prima la moltiplicazione della somma...
// se avvenisse l'opposto il risultato sarebbe diverso!

Referenze e sitografia

Nel pagina seguente si trova una lista completa delle precedenza degli operatori.
https://en.cppreference.com/w/cpp/language/operator_precedence

Matrici (array bidimensionali) in C++

Cos’è e come si memorizza e lavora con una matrice in C++?

Le matrici per farla breve non sono altro che array bidimensionali; cosa si intende per bidimensionali?

Capiamolo con un esempio: se per immaginare un classico array possiamo aiutarci pensando a una cassettiera i cui cassetti rappresentano le celle dell’array, per una matrice possiamo pensare a un insieme di cassettiere una a fianco all’altra, in cui analogamente ogni cassetto rappresenterà una cella della matrice.

Nelll’array ogni cassetto, e fuori di metafora ogni cella, sarà individuato da una coordinata ovvero da un numero che rappresenta l’ordine in altezza del cassetto nella cassettiera o la posizione della cella nell’array; in una matrice ogni cassetto o fupr di metafora ogni cella sarà individuata da una coppia di coordinate che rappresenteranno in quale cassettiera si trova un cassetto e di quale cassetto stiamo parlando ovvero in che riga e colonna della matrice si trova la cella prescelta.

Un altro esempio per provare a comprendere il significato di matrice è la griglia di battaglia navale, ogni casella della griglia è individuata dal numero della riga e dal numero della colonna.

Cos’è una matrice

Le matrici sono array bidimensionali ovvero insiemi ordinati* di r x c variabili dello stesso tipo, dove r è il numero di righe e c il numero di colonne.

* per ordinati non si intende i cui contenuti siano ordinati!

Vedremo poi che la bellezza delel matrici , come per gli array, è che ci si potrà muovere agevolmente tra le variabili interne di un array grazie a degli indici.

Dichiarare una matrice in C++

Per dichiarare una matrice bisogna utilizzare un’istruzione formata così:

tipoVariabile nomeMatrice [numeroRighe][numeroColonne];

Dove tipoVariabile è il tipo di dato di cui si vuole costruire la matrice (può essere int, char, float, double, bool…), mentre numeroRighe e numeroColonne devono essere dei valori costanti e rappresentano come dice il nome il numero di righe e di colonne della matrice.

Ad esempio:

//dichiaro una matrice m di int con 4 righe e 5 colonne
int m[4][5]

Come per gli array le celle delle righe e delle colonne si contano a partire da 0.

Per cui la matrice dell’esempio precedente avrà:
– 4 righe con i numeri di riga che andranno da 0 a 3,
– 5 colonne con i numeri di colonna che andranno da 0 a 4.

Lavorare con una cella della matrice in C++

Ogni cella della matrice funziona esattamente come una variabile, tuttavia per richiamarla bisogna usare il nome della matrice e indicare il numero di riga e di colonna della cella della matrice.
La seguente istrizione:

nomeMatrice [numeroRiga][numeroColonna] = valoreDaMemorizzare;

memorizza valoreDaMemorizzare nella cella della matrice nomeMatrice data dalle coordinate numeroRiga e numeroColonna 

Ad esempio l’istruzione:

m[2][4]=10;

memorizza nella cella della matrice m con nomero di riga 2 e numero di colonna 4 il valore 10.

Bisogna porre molta attenzione ai valori degli indici di riga e di colonna in C++ perché, a differenza rispetto a quanto avviene in altri linguaggi di programmazione, non vengono visualizzati errori o eccezioni se si superano i limiti del numero di righe o di colonne di una matrice, in questi casi si va a lavorare in delle aree di memoria dove chissà cosa è memorizzato.

Lavorare con tutte le celle di una matrice in C++

Per lavorare con tutte le celle di una matrice di norma la cosa più comoda è lavorare con due cicli for annidati uno dentro l’altro: uno servirà per scorrere gli indici di riga e l’altro per gli indici di colonna (o viceversa a seconda di quanto neessario).

Il seguente estratto di codice di esempio stampa il contenuto di una matrice m, una cella alla volta procedendo per riga e all’interno di ogni riga in ordine di ccrescente di colonna.

// il codice stampa il contenuto di una matrice m 
// con numeroDiRighe righe e numeroDiColonne colonne

// l'indice i scorre le righe
for(int i=0;i<numeroDiRighe;i++)
{
    //l'indice j scorre le colonne
    for(int j=0;j<numeroDiColonne;j++)
    {
        //si stampa la cella con riga i e colonna j
        cout<<m[i][j]<<" ";
    }
    //dopo ogni riga si va a capo
    cout<<endl;
}

Inizializzare una matrice in C++

inizializzazione da console

Per inizializzare una matrice a dei dati inseriti dall’utente da console di norma si procede con due for annidati:

//l'indice i scorre le righe
for(int i=0;i<numeroDiRighe;i++)
{
   //l'indice j scorre le colonne
   for(int j=0;j<numeroDiColonne;j++) 
   {
      //si memorizza il valore della cella
      //con riga i e colonna j di m
      cin>>m[i][j]; 
   }
}

Inizializzazione a zeri

Per inzializzare tutta a zero una matrice durante la dichiarazione si può usare la seguente istruzione:

int m[numeroDiRighe][numeroDiColonne]={};

che crerà una matrice m con numeroDiRighe righe e numeroDiColonne colonne con tutti zeri.

Inizializzazione a valori predefiniti

Allo stesso modo si può inizializzare una matrice a dei valori iniziali durante la dichiarazione inserendoli all’interno delle grafe divisi da virgole, questi verranno memorizzati in ordine nelle celle a partire dalla cella m[0][0] per poi passare alla cella m[0][1] fino a riempire la prima riga per poi passare alla seconda e così via, le cellenon inizializzate esplicitamente in questo caso saranno poste a zero.

Ad esempio l’estratto di codice

//si dichiara e inizializza la matrice
int m[4][3]={1,2,3,4,5};

//si stampa la matrice

//l'indice i scorre le righe
for(int i=0;i<4;i++) 
{
   //l'indice j scorre le colonne
   for(int j=0;j<3;j++) 
   {
      //si stampa la cella con riga i e colonna j di m
      cout<<m[i][j]<<" "; 
   }
//dopo ogni riga si va a capo
cout<<endl; 
}

stamperà:

1 2 3  
4 5 0 
0 0 0 
0 0 0

Esercizi con le matrici in C++

Se ora che hai capito come funzionano le matrici vuoi metterti alla prova vai alla pagina 20 esercizi matrici in C++ dove troverai un po’ di esercizi!

Video nel computer e formati video

In questo articolo andremo ad analizzare come vengono codificati video e in generale come sono memorizzati i file video all’interno del computer e dei nostri dispositivi elettronici.

Innanzi tutto dobbiamo pensare che un video è composto da una parte visiva e da una parte audio.

La parte visiva del video è composta da una sequenza di immagini, una dopo l’altra, dette anche fotogrammi o frame; una caratteristica importante è la frequenza di queste immagini, chiamata anche frame-rate, e si misura in fps (frame per secondo).

Curiosità: la televisione italiana usa 25 fps, mentre i film al cinema ne usano 24 fps, per questo motivo uno stesso film in TV dura un po’ meno (esattamente un venticinquesimo in meno ovvero ogni venticinque secondi se ne perde uno).

L’illusione di un movimento continuo/fluido nei video nasce dal fatto che le immagini da cui è composto un video vengono interpretate dal cervello, che in qualche modo le assembla e ne deduce il movimento anche se le immagini sono immagini distinte quindi i movimenti sono necessariamente “a scatti”.

La parte audio del video è costituita dalla rappresentazione di un suono, tramite le stesse modalità usate nei file audio, per una trattazione si rimanda alla pagina sulla rappresentazione di audio.

Codifiche

I video sono memorizzati e riprodotti attraverso i CODEC che sono programmi che si occupano di codificare e decodificare a seconda della richiesta un video per poterlo memorizzare o riprodurre.
I CODEC quindi codificano le informazioni spesso anche applicando delle compressioni che come nel caso della compressione delle immagini e quello della compressione degli audio possono essere senza perdite (lossless) o lossy (con predite).
Ovviamente sfrutteranno gli algoritmi per la compressione delle immagini e dell’audio, ma soprattutto per le immagini ce ne saranno ulteriori che ad esempio sfrutteranno le somiglianze tra un’immagine e la seguente.

Formati Video

Esistono vari formati, qui alcuni dei più utilizzati:
.AVI (Audio Video Interleave)
.MOV
.WMV o .WMA (Microsoft Windows Media)
.DIVX
.3GP. Il formato 3GP
.MP4 o MPEG-4
.MPEG-2
.DV e HDV.

Suoni nel computer e formati audio

In questo articolo andremo ad analizzare come vengono memorizzati i suoni e in generale come sono memorizzati i file audio all’interno del computer e dei nostri dispositivi elettronici.

Innanzi tutto premettiamo che ci sono due modalità: quella che memotizza gli spartiti e quella che memorizza le onde sonore.

Spartiti

Questa prima modalità consiste sostanzialmente nel memorizzare lo spartito di un brano musicale e si usa esclusivamente per la musica (soprattutto per le basi dei karaoke).
Se si vorrà ascoltare il file audio, lo sparito poi dovrà essere “suonato” dal computer attraverso appositi programmi che suonaeranno degli sturmenti sintetizzati e passeranno il risulatao alla scheda audio, il risultato sonoro dipenderà dalla scheda audio e dal programma utilizzato.

Gli svantaggi principali di questa tipologia di file audio sono che:
– la riproduzione denota una qualità che si distanzia notevolemente da quella dei suoni degli strumenti reali,
– molti suoni non si possono sscrivere su uno spartito e non sono riproducibili in questo modo

Una caratteristica positiva è che i documenti di questo tipo occupano pochissimo spazio, di fatto è come se si stessero memorizzando dei testi.

Il .midi è un formato libero e molto usato per questa tipologia di documenti audio.

Suoni

La seconda modalità consiste invece nel memorizzare all’interno del computer una rappresentazione dell’onda acustica di un suono, captata ad esempio da un microfono digitalizzando il suono tramite campionamento e discretizzazione.

Questa modalità permette di avere riproduzioni molto più simili ai suoni reali emessi dalla sorgente originaria di quel suono rispetto alla modalità con gli spartiti, ma non ne ha le limitazioni insite.

In questo caso la qualità dipende dalla frequenza di campionamento, che si misura in KiloHertz e generalmente è pari a 11, 22, 44.1, 48, 96 o 192 KHz, ovvero da ogni quanti secondi viene memorizzato il campione sonoro e dal numero di bit di codifica, detto anche bit-depth o risoluzione, ovvero quanti bit vengono utilizzati per memorizzare l’ampiezza di ogni singolo campione dell’onda sonora, pari generalmente a 8, 16 , 24 o 32 bit; un altro parametro importante il bit-rate che indica il numero di bit impiegati al secondo.

Gli audio con questa modalità in modo analogo a quanto avviene per la compressione delle immagini i documenti audio possono essere memorizzati tramite formati senza compressione o altri formati con compressione lossless (senza perdite) o lossy (con perdite).

Formati senza compressione

Formati senza compressione sono il .wav o .wave e il .aif.
Nei CD audio il formato utilizzato è il .wav a 16 bit con una frequenza di campionamento di 44.100 Hz.

Formati con compressione lossless

I formati senza compressione non hanno riscosso molto successo dal punto di vista della diffusione, tra i vari ricordiamo .flac (generalmente il risparmio di memoria è troppo basso per giustificare le operazioni perottenerlo).

Formati con compressione lossy

I formati con compressione lossy sono i formati più diffusi perché consentono un buon risparmio di memoria pur mantendo una qualità accettabile per i più, tra questi formati ricordiamo i più diffusi: .mp3, .wma e .ogg.

In questi formati sostanzialmente vengono eliminate alcune frequenze a partire da quelle inudibili per l’uomo al fine di ridurre i dati da memorizzare, più la compressione è maggiore più la qualità audio peggiorerà. Di solito la qualità di questi formati viene misurata con il numero di bit impiegati al  secondo (bit-rate) valori tipici sono: 128 kbps, 160 kbps, 192 kbps o 320 kbps.