Corso "Programmazione A Oggetti in C++"
Corso "Programmazione A Oggetti in C++"
Scaricare il materiale da
http://www.fisica.unisalento.it/~denunzio/allow_listing/CORSO_C++/
[LEZIONE 1]
https://itsfoss.com/c-plus-plus-ubuntu/
g++ --version
g++ -v
In caso di messaggio d’errore del tipo “command not found”, occorre installare gli strumenti di
compilazione, in particolare mediante il pacchetto build-essential (in realtà la maggior parte delle
volte questo step è superfluo: può essere ad esempio necessario in caso di upgrade da una versione a
un’altra del sistema operativo):
Per editare i sorgenti, usare un editor come gedit, poi lavorare manualmente da riga di comando.
gedit exer01.cpp
Chi usi il sottosistema linux di Windows, troverà i file linux in una directory Windows situata
grosso modo a:
C:\Users\Giorgio De Nunzio\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\giorgio\
Si consiglia di creare un link alla directory, ponendolo sul desktop. E’ fondamentale non creare da
Windows i file da usare in Linux, bensì crearli in linux (ad esempio con il comando touch) e (per
comodità) una volta creati editarli da Windows, con notepad o notepad++ o altro editor.
#include <iostream>
using namespace std;
int main()
{
cout << "Hello!\n";
return 0;
}
g++ exer01.cpp
ed equivalentemente
In caso di messaggio di errore perché g++ non è nel PATH, rimpiazzare il comando g++ con:
/usr/bin/g++
2
Anche gcc può compilare il C++:
./exer01
#include <iostream>
using namespace std;
int main()
{
int x1, x2;
cout << "Hello!\n";
cout << "Insert a number: " << endl;
cin >> x1;
cout << "Insert another number: " << endl;
cin >> x2;
cout << "The sum is: " << x1 + x2 << endl;
return 0;
}
http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
3
c. The assembler code generated by the compiler is assembled into
the object code for the platform.
d. The object code file generated by the assembler is linked together
with the object code files for any library functions used to produce
an executable file.
1. To stop the process after the preprocessor step, you can use the -
E option:
2. g++ -Wall -std=c++11 -E prog1.cpp
3. To stop the process after the compile step, you can use the -
S option:
4. g++ -Wall -std=c++11 -S prog1.cpp
5. To stop the process after the assembly step, you can use the -
c option:
6. g++ -Wall -std=c++11 -c prog1.cpp
Sams,.Teach.Yourself.C++.For.Linux. pag 8
4
Programmi composti da più sorgenti (in file diversi)
Vedere innanzitutto le opzioni di compilazione intermedia; oltre che la pagina Web vista in
precedenza, un’altra fonte di informazioni è: Tom_Swans_GNU_C++_for_Linux (pag 99)
exer03.cpp
#include <iostream>
#include "c3.h" // <-- remark the difference from the preceding line
using namespace std;
int main()
{
double x1, x2; // <-- double instead of int
cout << "Hello!\n";
cout << "Insert a number: " << endl;
cin >> x1;
cout << "Insert another number: " << endl;
cin >> x2;
cout << "The sum is: " << x1 + x2 << endl;
cout << "Now sum the squares\n";
double x1q = calculateSquare(x1);
double x2q = calculateSquare(x2);
cout << "The sum of the squares is: " << x1q + x2q << endl;
return 0;
}
5
c3.h
c3.cpp
#include <iostream>
#include "c3.h"
using namespace std;
double calculateSquare(double x)
{
cout << " hi! I calculate squares!" << endl;
double xs = x * x;
cout << xs << endl;
return xs;
}
Questo comando compila i due file .cpp, effettua il link, e salva l’eseguibile con il nome eser03.
./eser03
Che succede se si omette l'include di c3.h in exer03.cpp? Che succede se si omette la compilazione
del file c3.cpp (eliminando cioè c3.cpp dalla riga di comando riassuntiva)?
#include <iomanip>
cout << setw(8); // larghezza del campo di output
cout << setprecision(3); // num. cifre decimali compreso il “.” (se non “fixed”)
Esempio completo:
// setprecision example
#include <iostream> // std::cout, std::fixed
#include <iomanip> // std::setprecision
6
int main () {
double f =3.14159;
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
std::cout << std::fixed; // ora il numero indica solo le cifre decimali
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
return 0;
}
#include <stdio.h>
int main()
{
printf("%7.2f\n", 123.456);
}
Inserire ora le medesime istruzioni di compilazione in un file chiamato (per esempio) buildit2,
da rendere eseguibile con chmod (vedi dopo), e da richiamare con ./buildit
#!/bin/sh
# call me with: ./buildit
g++ -o exer03.o exer03.cpp -c
g++ -o c3.o c3.cpp -c
g++ -o exer03 exer03.o c3.o
chmod
https://www.mrwebmaster.it/linux/gestire-permessi-chmod-chown-chgrp_10211.html
MAKE:
7
A Simple Makefile Tutorial (da modificare per tener conto della sintassi C++)
http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
clean:
rm *.o exer03
Nel makefile si riconoscono: target, dipendenze (dependencies) e regole (rules). La linea delle
regole dev’essere indentata rigorosamente da un tab.
#include <iostream>
using namespace std;
int main()
{
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;
8
cout << "sizeof char == " << sizeof(c) << endl;
cout << "sizeof short == " << sizeof(s) << endl;
cout << "sizeof int == " << sizeof(i) << endl;
cout << "sizeof long == " << sizeof(l) << endl;
cout << "sizeof float == " << sizeof(f) << endl;
cout << "sizeof double == " << sizeof(d) << endl;
cout << "sizeof long double == " << sizeof(ld) << endl;
return 0;
}
http://www.math.unipd.it/~sperduti/CORSO-C%2B%2B/Variabili_%20Tipi%20di%20dato_%20Costanti.htm
* I valori nelle colonne Byte e Range possono variare a seconda del sistema. I valori quì
riportati sono quelli più comunemente accettati ed usati da quasi tutti i compilatori.
9
Oltre a questi tipi di dato fondamentali vi sono anche i puntatori e il tipo void.
[LEZIONE 2]
ARRAY / STD::VECTOR
Now discuss the first part of the longer “Example” program reported later, up to (and
without including) the STL part. Then come back here for a simple example with STL.
int j;
for (j = 0; j < 5; j++)
cin >> x[j]; // x.at(j) gives out of range
10
Now discuss the second part of the “Example” program: what is STL, with some
commentaries).
“Object Oriented: Does support all object oriented features like inheritance, polymorphism,
encapsulation, data hiding and abstraction.
Object Based: Does not support inheritance and polymorphism.”
Example
#include <iostream>
#include <vector>
#include <algorithm> // find, reverse, count, max_element
#include <numeric> // accumulate,
int v1[10];
int n;
n = 10;
// or
// cin >> n;
int v2[n];
int k = 10;
int *v3 = new int[k];
int *v3a = new int [4] {1, 2, 4, 5}; // C++0x, the vector size is compulsory
// use the arrays vith v3[3], *v3, *(v3+2)...
delete[] v3a;
delete[] v3;
// Various notes
cout << sizeof(v2)/sizeof(int) << endl; // to calculate the length of an array (statically allocated)
11
for (int j(0); j < n; j++) // <-- direct initialization of j, C++
v1[j] = j;
for (int j = 0; j < n; j++) // <-- copy initialization
cout << *(v1+j) << endl;
v.push_back(100);
cout << "after v.push_back(100)" << endl;
v.push_back(100);
cout << "after v.push_back(100)" << endl;
// empty()
cout << "is v empty?" << endl;
cout << v.empty() << endl;
// pop_back
v.pop_back(); // removes the last element
vector<int> vvv = vv; // !!!!! Great! Try and do this with C arrays....
cout << vvv[5] << endl;
12
cout << endl;
// Use v.at()
// The at() operation is a vector subscript operation that throws an exception of type
// out_of_range if its argument is out of the vector's range
// ITERATORS
// In a loop such as
// for (int j = 0; j < v.size(); j++) cout << v[j] << endl;
// j is used to access v's elements sequentially, but the compiler cannot know
// (so at each iteration &v[j] must be calculated).
//
// Iterators make this situation explicit, so optimizing it.
// They are similar to pointers.
printIntVector1(v);
// Every standard container, such as vector, defines two associated iterator types:
// container-type::const_iterator ("::" is the Scope Resolution Operator)
// container-type::iterator
// When we want to use an iterator to change the values stored
// in the container, we use the iterator type.
// If we need only read access, then we use the const_iterator type.
// v.begin() and v.end() return iterators, which are automatically converted to const_iterator
// They return a value that denotes the beginning or (one past) the end of a container.
// Dereference operator, *
printIntVector1(v);
// IMPORTANT! erase returns an iterator that is positioned on the element that follows the one
// that we just erased. The iterator used for deletion is invalidated: we know that iter can no
// longer refer to the same element because that element is gone. In fact, calling erase on a
// vector invalidates all iterators that refer to elements after the one that was just erased.
// I MUST USE a while loop in this example, no for loop, because iter will not always be incremented....
cout << "Define a new vector" << endl;
printIntVector1(vx);
13
cout << "Now erase elements > 100" << endl;
vector<int>::const_iterator iter = vx.begin();
while (iter != vx.end()) // remark that vx.end() MUST be evaluated at each loop! Use no temp variable
{
if (*iter > 100) // Only erase elements > 100
iter = vx.erase(iter); // iter now points to the element AFTER the erased one
else
iter++;
}
printIntVector1(vx);
printIntVector1(vx);
printIntVector1(vx);
// GENERIC ALGORITHMS
cout << "GENERIC ALGORITHMS" << endl;
// Now use find and insert, to insert a vector of three numbers {997 998 999}
// after the first occurrence of value 125
cout << "insert a vector of three numbers {997 998 999} after the first occurrence of value 125\n";
vector<int> vi = {997, 998, 999};
vector<int>::const_iterator it1 = find(vx.begin(), vx.end(), 125); // find element 125
if (it1 != vx.end()) {
cout << "element found! It is at index " << it1 - vx.begin() << endl;
cout << "Now insert the vi vector\n";
vx.insert(it1+1, vi.begin(), vi.end()); // it1 + 1 points to the element after 125 in vx!
}
printIntVector1(vx);
//
// Other algorithms:
14
//auto maxit = max_element(vx.begin(), vx.end());
cout << *maxit << endl;
// Multidimensional vectors
vector <vector <int> > v2d(3);
return 0;
}
void printIntVector1(vector<int> v)
{
// remember that the end() iterator points PAST the end of the vector
// an iterator is defined as container-type::const_iterator or container-type::iterator
// cout the vector
//cout << sizeof(v) << endl;
for (vector<int>::const_iterator iter = v.begin(); iter != v.end(); iter++)
cout << *iter << " ";
cout << endl;
}
return 0;
}
Declaring a pointer and storing the address of a variable in a pointer, int *ip = &n;
Use of ++ and -- on a pointer (if memory is layed out in increasing addresses with the
chronological order of variable definition)
int n1 = 101, n2 = 102, n3 = 103;
int *ip = &n1;
cout << ip << " " << *ip << endl;
ip++;
cout << ip << " " << *ip << endl;
ip++;
cout << ip << " " << *ip << endl;
15
int nn[10];
for (int j = 0; j < 10; j++)
nn[j] = j;
for (int j = 0; j < 10; j++)
cout << nn[j] << endl;
cout << endl;
int *ip1 = &nn[0];
int *ip2 = nn;
for (int j = 0; j < 10; j++)
cout << *ip2++ << endl;
Remark: functions.
#include <iostream>
int main()
{
int x = 133;
cout << "MAIN: x is " << x << endl;
changeAFunctionArgument1(x);
cout << "MAIN: x is " << x << endl;
changeAFunctionArgument2(&x);
cout << "MAIN: x is " << x << endl;
changeAFunctionArgument3(x);
cout << "MAIN: x is " << x << endl;
return 0;
}
int main()
{
double w[] = {61, 55, 6, 3, 2, 5, 6};
cout << "max is " << theMax1(w, 7) << endl;
cout << "max is " << theMax2(w, 7) << endl;
return 0;
}
16
return maxVal;
}
EXERCISES:
2.1) Write a program that asks the user to enter some “double” numbers (the number of values
entered is asked to the user with a first question: "how many numbers do you want to enter?"),
Calculate the average, find the minimum and the maximum, write everything on screen.
Write three versions:
a. in the first version use the C arrays, statically allocated but with the number of
elements contained in a variable entered by the user n (i.e.: int n; cin >> n;
double x[n];) and write the functions calculating the average and max / min
starting from the basic C/C++ functions (max/min is not trivial, the first time you
do!)
b. the second version is like the first, but using C ++ vectors;
c. the third is like the second, but you use accumulate to make the sum,
max_element and min_element for maximum and minimum;
If necessary (in case of compilation errors), use options g++ -std=c++11, g++ -std=c++0x,
g++ -std=gnu++11 or g++ -std=gnu++0x
2.2) Write a program that finds (and inserts into a vector<...>) the prime numbers lower than a
maximum value ‘maxv’ entered by the user; it should be structured as a main function that asks the
user for the maxv value and then calls a findPrimes function that receives this value as input and
returns a vector of integers; the main program, then, prints the vector of the prime numbers to the
screen by using a printVector function. In a first version, put the three functions (main,
findPrimes, and printVector) in a single file; in a second version, put the three functions in
three different files, and write a Makefile to achieve compilation. Write findPrimes both using
iterators and using indexing.
Remember that finding prime numbers may involve two approaches:
a. this one is simpler from the point of view of programming, but it is computationally less
efficient: (after considering that 1, 2, and 3 are prime and are particular cases) for each
number n from 4 to maxv (here is a first for loop!), consider the integer numbers d from
2 to n or (far better!) to floor(sqrt(n)) (a second for loop!) and check if n may be
divided by d. If you find a d for which it can, number n is not prime. If you want, you
may use another level of function, say a bool isNumberPrime(int d) function,
which contains the second for loop and returns true or false if the d number is prime or
not. For the calculation of the integer part of the square root of a number you may use
#include <cmath> and floor(sqrt(x)).
Here is the isNumberPrime function:
bool isNumberPrime(int d)
{
bool isPrime = true;
for (int j = 2; j <= floor(sqrt(d)); j++) {
// cout << ".... " << j << " ";
if (d%j == 0)
17
isPrime = false;
// cout << d%j << " " << isPrime << endl;
}
return isPrime;
}
b. The Sieve of Eratosthenes (https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)
Modify the code so that the maxv value is given through the command line parameters.
[LEZIONE 3]
// C-style strings
// A string is a char array terminated by a null character '\0' (or 0) (null-terminated char array).
// argv -> p1 p2 p3
// | | |
// V V V
// 'e' 'g' 'm'
// 'x' 'o' 'o'
// '1' 'o' 'o'
// 0 'd' 'r'
// 0 'n'
// 'i'
// 'n'
// 'g'
// 0
You may also have e.g. int **p, which is a pointer to pointers to integers, and is used to define
a multidimensional array (a vector of vectors of integers).
C language:
https://www.geeksforgeeks.org/dynamic-memory-allocation-in-c-using-malloc-calloc-free-and-realloc/
// Ask for 100 integer variables. The ptr pointer will hold the
// base address of the block created.
// If space is insufficient, allocation fails and malloc
// returns a NULL pointer
C++ language:
#include <iostream>
// print out
for (j = 0; j < 10; j++)
cout << ptr[j] << endl;
cout << endl;
it *ptr2 = ptr;
//for (j = 0; j < 10; j++)
// *ptr2++ = 111; // ptr is updated!! munmap_chunk(): invalid pointer
19
////////ptr -= 10;
// print out
///for (j = 0; j < 10; j++)
cout << ptr[j] << endl;
return 0;
}
ARRAY MULTIDIMENSIONALI
https://www.programiz.com/cpp-programming/multidimensional-arrays
table with 3 rows and each row has 4 columns as shown below.
20
int test[2][3][4] = { // better
{ {3, 4, 2, 3}, {0, -3, 9, 11}, {23, 12, 23, 2} },
{ {13, 4, 56, 3}, {5, 9, 3, 5}, {3, 1, 4, 9} }
};
int main()
{
int test[3][2] =
{
{2, -5},
{4, 0},
{9, 1}
};
return 0;
}
TO BE DONE
TO BE DONE
STRINGS
Da http://www.ntu.edu.sg/home/ehchua/programming/index.html
http://www.ntu.edu.sg/home/ehchua/programming/cpp/cp9_String.html
Characters and Strings
1. The C-style string (or C-String) in header cstring (ported over from C's string.h), which represents a
string as a char array terminated by a null character '\0' (or 0) (null-terminated char array).
2. The new C++ string class in header string. string is a regular class, with public interface defined in
the constructors and public member functions.
C-String
int main() {
// char * str1 = "hello";
// warning/error: deprecated conversion from string constant to 'char*'
21
const char * str2 = const_cast<char *>("hello"); // remove the "const"
C-string (null-terminated char array) can be declared as char* or char[]. This is because C treats an array
name as a pointer to the first element of the array.
Unlike regular arrays, there is no need to pass the length of C-string into function, as the function can deduce
the length from the terminating null character.
toupper, tolower
22
The following example defines and uses both C-strings and C++ strings
#include <iostream>
#include <string> // C++ string class
#include <cstring> // C-string
using namespace std;
int main() {
char cstr[] = "hello world!"; // C-string literal
s7d.clear();
cout << s7d.empty() << endl;
string s99;
s99 = s5a.substr(2, 4);
cout << s99 << endl;
23
// Conversion of string to C-string
c_str;
Some functions, such as ofstream's open() which opens a file, accept only C-string. You can use c_str to
get a C-string from a string object:
str99.c_str()
Here is an example:
string mystring;
mystring = string(argv[1]);
string mystring(argv[1]);
Big example
/*
* Guess a secret word (WordGuess.cpp)
*/
#include <iostream>
#include <string>
#include <cstdlib> // srand, rand http://www.cplusplus.com/reference/cstdlib/rand/?kw=rand
#include <ctime> // time
#include <cctype>
using namespace std;
int main() {
// Seed the pseudo-random number generator
srand(time(0)); // http://www.cplusplus.com/reference/ctime/time/?kw=time
24
|| attempt.find(letter) != string::npos) {
cout << "You already use this letter. Try again.\n";
continue; // get out of the while loop!!!
} // if badChars.find...
// The letter had not been used before: Check for good or bad letter
int pos = target.find(letter);
if (pos == string::npos) { // not found
cout << "Oh, bad guess!\n";
badChars += letter; // add to badChars string: append!
} else {
cout << "Good guess!\n";
attempt[pos] = letter;
// Check if this letter appears again later
do {
pos = target.find(letter, pos + 1);
if (pos != string::npos) attempt[pos] = letter;
} while (pos != string::npos);
} // else
++trial;
} // while
cout << "You got it in " << trial << " trials! The secret word is \""
<< target << "\"" << endl;
char playAgain;
cout << "Another game? <y/n> ";
cin >> playAgain;
if (playAgain != 'y' && playAgain != 'Y') over = true;
} while (!over);
Altre funzioni:
http://www.ntu.edu.sg/home/ehchua/programming/cpp/cp9_String.html
append
insert
assign
erase
replace
swap
push_back
pop_back
compare
find
.....
FILE I/O
http://www.ntu.edu.sg/home/ehchua/programming/cpp/cp10_IO.html
http://www.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/38IOCPP.html
C++ handles file IO similar to standard IO. You need to include both <iostream> and <fstream> headers in your
program for file IO.
25
To write to a file, you construct a ofstream object connecting to the output file (file opening), and use the ostream
functions such as stream insertion <<, put() and write(). Then the connection is removed by file closing.
Similarly, to read from an input file, construct an ifstream object connecting to the input file (file opening), and use the
istream functions such as stream extraction >>, get(), getline() and read().Then the connection is removed by file
closing.
File Output
The steps are:
#include <fstream>
.......
ofstream fout;
fout.open(filename, mode);
......
fout.close();
By default, opening an output file creates a new file if the filename does not exist; or truncates it (clear its content) and
starts writing as an empty file.
File Input
The steps are:
#include <fstream>
.......
ifstream fin;
fin.open(filename, mode);
......
fin.close();
File Modes:
Text I/O
int main() {
string filename = "test.txt";
// Write to File
ofstream fout(filename.c_str()); // default mode is ios::out | ios::trunc
if (!fout) {
cerr << "error: open file for output failed!" << endl;
abort(); // in <cstdlib> header
}
fout << "apple" << " pineapple" << endl;
fout << "orange" << endl;
fout << "banana" << endl;
fout.close();
return 0;
}
Binary I/O
TO BE DONE
EXERCISES:
27
3.1) Modify the file writing / reading example so that the code acquires the file name from the user,
by means of a message to the user and the use of stdin (cin >>). Then, edit it again so that it
acquires the file name from the command line (with argc / argv). Finally modify it so that it
acquires the file name from the command line but, if the parameter is not passed by the user, then it
explicitly asks for it with cin >>. Pay attention to the nature of the string (C-string or string) in the
various cases.
3.2) Write a program composed of three different files: main.cpp, write.cpp, reread.cpp, containing
respectively three homonymous functions. The main() program calls sequentially write() and
reread(). write () asks the user for three values: a, b, d, and saves a text table to a file on disk,
containing two columns: the first reports the values between a and b with step d, and the second
showing the corresponding cosines. reread () reads the table and displays it on the screen. Do not
use standard I/O and redirection, but I/O to a text file.
[LEZIONE 4]
Fonte: http://www.crittologia.eu/critto/caesar.htm
Svetonio, nella “Vita dei dodici Cesari”, racconta che Giulio Cesare usava, per le sue corrispondenze
riservate, una cifra (sistema di cifratura, ndGDN) monoalfabetica molto semplice, nella quale la lettera chiara
viene sostituita dalla lettera che la segue di tre posti nell'alfabeto: la lettera A è sostituita dalla D, la B dalla E
e così via fino alle ultime lettere che sono cifrate con le prime come nella tabella che segue (che fa
riferimento all'odierno alfabeto internazionale). Svetonio non dice nulla su come cifrare le ultime lettere
dell'alfabeto; di solito si intende che si proceda circolarmente ricominciando dalla A come nella lista
seguente:
Chiaro ABCDEFGHIJKLMNOPQRSTUVWXYZ
Cifrato DEFGHIJKLMNOPQRSTUVWXYZABC
Prendendo come esempio la frase “Auguri di buon compleanno” ed eliminando gli spazi, si otterrà il
seguente messaggio cifrato:
Chiaro AUGURIDIBUONCOMPLEANNO
Cifrato DXJXULGLEXRQFRPSOHDQQR
Più in generale si dice cifrario di Cesare una cifra nella quale la lettera del messaggio chiaro viene spostata di
un numero fisso di posti, non necessariamente tre; un esempio è la cifra che, sempre secondo Svetonio era
usata da Augusto, dove la A era sostituita dalla B, la B dalla C fino all'ultima lettera dell'alfabeto latino, la X,
che era sostituita da una doppia A.
Poiché l'alfabeto internazionale è composto da 26 caratteri sono possibili 26 cifrari di Cesare diversi dei quali
uno (quello che comporta uno spostamento di zero posizioni) darà un cifrato uguale al messaggio chiaro
iniziale. Dal punto di vista della crittanalisi il cifrario di Cesare è debolissimo essendoci solo 25 cifrari
diversi.
28
sia costituito da un int main() e da una funzione char cifracarattere(char,
int)
il main() acquisisca da riga di comando il nome di un file, creato dall’utente in maniera
manuale, che contenga nella prima riga la chiave (valore numerico intero) e nelle righe
successive il testo da criptare, che supponiamo per ora composto solo da lettere maiuscole
ed eventualmente spazi, ad esempio:
3
AUGURI DI BUON COMPLEANNO
dopo aver letto la chiave come int, allochi una stringa che conterrà il risultato della cifratura
leggendo via via le “parole” contenute nel file, cicli sui caratteri di ciascuna parola,
determinando, mediante la funzione cifracarattere(), il corrispondente carattere
cifrato, e inserisca tale carattere cifrato nella stringa di output;
leggendo il file di input per parole, automaticamente avverrà la non-criptazione degli spazi,
che saranno ignorati;
nell’esempio considerato, la stringa a questo punto sarà DXJXULGLEXRQFRPSOHDQQR
finita la lettura del file di input, alla stringa di output sarà accodato un carattere il cui posto
nell’alfabeto è il medesimo della chiave: essendo la chiave nel caso considerato pari a 3, la
stringa di output a questo punto vale: DXJXULGLEXRQFRPSOHDQQRC
una volta costruita la stringa di output, il programma apra in scrittura un file, il cui nome
sarà dato dal nome del file di input preceduto dalla stringa “coded_”: per esempio, se il file
di input si chiama “text.txt”, il nome del file di output deve essere “coded_text.txt”; si
supponga, per semplicità, che il file di input sia situato nella medesima directory del
programma eseguibile, per cui non vi saranno problemi di path più o meno complessi;
nel file testé creato, il programma salvi la stringa cifrata
La funzione cifracarattere() conterrà al suo interno una stringa costante,
denominata alfabeto, definita come “ABCDEFGHIJKLMNOPQRSTUVWXYZ”; ricevuto
un carattere da criptare, la funzione cercherà nella stringa la posizione di tale carattere,
sommerà la chiave (che potrebbe anche essere negativa), e prenderà il resto della divisione
per 26 (lunghezza dell’alfabeto), ottenendo l’indice del carattere desiderato nell’alfabeto;
prenderà quindi il carattere corrispondente e lo restituirà in output: in questo modo è
realizzata la circolarità del codice (dopo la Z c’è la A, prima della A c’è la Z). Nota
importante: il sistema funziona anche per numeri negativi! Supponiamo che nel calcolo
otteniamo come carattere codificato il numero -1, ossia il carattere che precede la A: se
calcoliamo -1%26 abbiamo 25, che è proprio la Z. Nota ulteriore: per ottenere il carattere
corrispondente alla chiave, richiesto per “tappare” la stringa di output, è possibile adoperare
la medesima funzione cifracarattere(): come?
Ottenuto il software per la crittazione, si scriva il software per la decrittazione, per esempio
deCifrarioCesare(): il main acquisisca da riga di comando il nome del file
contenente il testo crittato, carichi il testo (un’unica stringa senza spazi, creata dal
programma descritto in precedenza), ne prenda l’ultimo carattere per riconoscere la chiave,
usi la chiave cambiata di segno per effettuare la decodifica con la medesima funzione
cifracarattere(), e salvi un file con il nome preceduto da decoded_ contenente il
testo, naturalmente privo degli spazi originali.
Per riconoscere la chiave a partire dall’ultimo carattere del testo crittato, occorre accedere
all’alfabeto usato dalla funzione cifracarattere(); diverse soluzioni sono possibili:
nella più semplice l’alfabeto è una variabile (in realtà const) globale, oppure, meglio, una
29
#define da inserire in un file a parte (alfabeto.h) da includere sia nella
cifracarattere() che nella funzione deCifrarioCesare().
Se proprio vogliamo esagerare, realizziamo un terzo software che prenda i due file originale
e decrittato, e scopra se il contenuto, a meno degli spazi, è uguale!
Note:
In alternativa all’uso della stringa alfabeto, è possibile tenere conto del fatto che i caratteri
alfanumerici hanno un corrispondente numerico nel codice ASCII (che è poi il valore contenuto
nelle variabili char), per cui è possibile direttamente realizzare calcoli aritmetici sui char.
[LEZIONE 5]
https://www.html.it/guide/guida-c2/
Introduzione
Principio di incapsulamento: una classe definisce un’interfaccia verso il mondo esterno, che
consente di manipolarne di dati e, allo stesso tempo, maschera le dinamiche del suo funzionamento
interno.
Nel linguaggio C++ tutto ciò è reso possibile dal costrutto class, la cui sintassi è la seguente:
class <nome>
30
{
public:
// membri accessibili all'esterno della classe
protected:
// membri accessibili dalla classe ed in tutte le sue derivate
private:
// membri accessibili solo all'interno della classe
};
Le tre sezioni distinte (public etc) (non necessariamente tutte presenti) servono per la definizione
del livello di accesso o visibilità di ogni membro: pubblico, protetto e privato. Come impostazione
predefinita, se nessuna delle tre sezioni viene indicata esplicitamente, i membri della classe sono
privati (differenza con le struct!).
I dettagli si comprenderanno quando si studieranno l’ereditarietà e le classi derivate.
Per ragioni storiche e tecniche in C++ è preferibile scomporre il codice relativo ad una classe in due
file, uno contenente la definizione dell’interfaccia (detto header) e l’altro l’implementazione dei
metodi. Per convenzione, il nome di entrambi tali file è dato dal nome della classe in esso
contenuta, con estensione .h e .cpp rispettivamente. Alle volte il nome della classe è preceduto da
“C”.
Esempio in un unico file.
Classe rettangolo
Sovraccarico (overloading) di una funzione membro (un metodo) (in questo caso, il
costruttore)
#include <iostream>
using namespace std;
class rettangolo
{
private:
double a, b;
public:
rettangolo()
{
cout << "Sono il costruttore di default\n";
a = b = 0;
}
~rettangolo()
{
cout << "Sono il distruttore\n";
double getA()
{
return a;
}
} ;
int main()
{
31
rettangolo r1;
rettangolo r2(5,10);
cout << r1.getA() << endl;
cout << r2.getA() << endl;
return 0;
}
La definizione della classe è compresa tra le cosiddette “include guards”: direttive al preprocessore
che hanno lo scopo di impedire che il file header venga incluso più volte durante la compilazione
dell’eseguibile, causando problemi di definizione multipla della stessa classe.
Per evitare di generare collisioni con altri nomi, è sempre saggio definire un namespace che
contiene le definizioni delle nostre classi. In questo caso la classe appartiene al namespace
Geometry, come dichiarato subito dopo le include guards. Usiamo una dichiarazione anticipata
all’interno del namespace per spostare la definizione della classe all’esterno di esso, evitando così
un ulteriore livello di indentazione. Si noti che questa è una scelta stilistica che ci obbliga ad usare il
prefisso Geometry:: ogni volta che il nome della classe ricorre nel listato. Nulla vieta, però, di
definire la classe entro il blocco che delimita il namespace, se lo si preferisce.
La classe contiene due membri privati di tipo double, denominati x e y, e dei metodi pubblici per
l’accesso a tali membri rispettivamente in lettura, X() e Y(), ed in scrittura, setX() e setY().
È inoltre presente un metodo pubblico per il calcolo della distanza euclidea rispetto alle coordinate
di un altro oggetto di tipo Point2D. La definizione della classe deve essere seguita dal simbolo ; per
ragioni di uniformità e retrocompatibilità con la definizione di struct mutuata dal linguaggio C, che
in C++ è un costrutto equivalente a quello di classe, seppure con delle piccole differenze
(essenzialmente, per default tutti i membri sono pubblici).
L’implementazione dei metodi di classe è contenuta nel file point2D.cpp, riportato nel listato
seguente:
// Point2D.cpp
32
#include "Point2D.h"
#include <cmath>
double Geometry::Point2D::X() {
return x;
}
void Geometry::Point2D::setX(double value) {
if (!std::isnan(value) && !std::isinf(value))
x = value;
else
x = 0;
}
double Geometry::Point2D::Y() {
return y;
}
void Geometry::Point2D::setY(double value) {
if (!std::isnan(value) && !std::isinf(value))
y = value;
else
y = 0;
}
double Geometry::Point2D::distanceFrom(Point2D other)
{
return std::sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
}
Il file .cpp relativo all’implementazione di una classe deve includere il file header della classe, e da
tutti gli altri header necessari, affinché il compilatore possa risolvere correttamente i nomi dei
simboli in esso contenuti.
La definizione di membri interni privati e dei corrispettivi metodi getter e setter è una pratica
comune nella definizione di classi che serve a garantire che, qualora sia necessario modificare i
valori di x e y, ciò avvenga in maniera “controllata”. Ad esempio, in questo caso, i valori da
assegnare come coordinate del punto vengono prima validati.
Se x e y fossero stati membri pubblici, sarebbe stato necessario ripetere questo tipo di validazione
ogni volta che ad essi fosse stato assegnato un nuovo valore, aumentando così le possibilità di
errore. Come ulteriore beneficio della presenza di tali metodi e della validazione che essi operano, il
risultato della funzione distanceFrom sarà sempre un valore numerico valido.
// testPoint2D.cpp
#include <iostream>
#include "Point2D.h"
using Geometry::Point2D;
//using namespace Geometry;
// or put Geometry:: before names coming from this namespace
using namespace std;
int main() {
Point2D p;
p.setX(1);
33
p.setY(1);
Point2D p2;
p2.setX(2);
p2.setY(2);
Compilare con
g++ -o testPoint2D testPoint2D.cpp Point2D.cpp
namespace Geometry {
class Point2D;
}
class Geometry::Point2D
{
public:
double X();
void setX(double value);
double Y();
void setY(double value);
double distanceFrom(Point2D other);
private:
double x;
double y;
};
#include <cmath>
double Geometry::Point2D::X() {
return x;
}
void Geometry::Point2D::setX(double value) {
if (!std::isnan(value) && !std::isinf(value))
x = value;
else
x = 0;
}
double Geometry::Point2D::Y() {
return y;
}
void Geometry::Point2D::setY(double value) {
if (!std::isnan(value) && !std::isinf(value))
y = value;
else
y = 0;
}
double Geometry::Point2D::distanceFrom(Point2D other)
{
return std::sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
}
using Geometry::Point2D;
int main()
{
Point2D p;
p.setX(1); p.setY(1);
34
Point2D p2;
p2.setX(2); p2.setY(2);
[LEZIONE 6]
Prerequisito
per valore: il valore di una variabile o costante, parametro effettivo, è copiato nel parametro
formale della funzione (e quindi nella corrispondente variabile locale); se il parametro effettivo è
una variabile, il suo contenuto non può essere modificato dalla funzione.
per indirizzo: un puntatore alla variabile parametro effettivo, è passato nel parametro formale della
funzione; il parametro effettivo (o, meglio, il valore da esso puntato) può in linea di principio essere
modificato dalla funzione, salvo se si fa uso dell’attributo const (vantaggi: usa poca memoria,
permette ad una funzione di restituire più di un valore; svantaggio: “pericoloso”, sintassi meno
immediata). In realtà è solo un passaggio per valore, in cui però il valore è quello del puntatore a
una variabile (tipico del C!).
per riferimento: la funzionalità è analoga al passaggio per indirizzo, ma con una sintassi più
semplice (C++).
I tipi dei dati (passati alla funzione) devono concordare con quelli previsti, a meno di possibili
conversioni automatiche o di cast espliciti.
35
return(0);
}
Per passare parametri per riferimento (e analogamente per indirizzo) allo scopo di non muovere
grosse zone di memoria (quindi, non desiderando l’alterazione del valore del parametro) è
consigliabile usare una dichiarazione const.
void scambia (const int &x, const int &y){ ...
Costruttori (continuazione)
https://www.html.it/pag/15519/costruttori-e-distruttori/
// point2d.cpp
#include "point2d.h"
#include <cmath>
Geometry::Point2D::Point2D()
{
x = 0;
y = 0;
}
Geometry::Point2D::Point2D(double xVal, double yVal)
{
setX(xVal);
setY(yVal);
}
void Geometry::Point2D::setX(double value) {
if (!std::isnan(value) && !std::isinf(value))
x = value;
else
x = 0;
}
void Geometry::Point2D::setY(double value) {
if (!std::isnan(value) && !std::isinf(value))
y = value;
else
y = 0;
}
// altro...
// test04a.cpp
class A {
public:
A();
~A();
private:
int m;
int *ptr;
int *ptr2;
};
A::A()
{
37
m = 0;
ptr = new int;
ptr2 = new int[1000000];
}
A::~A()
{
delete ptr;
delete [] ptr2;
}
int main()
{
A a;
return 0;
}
E giacchè stiamo parlando di allocazione dinamica, vediamo se è proprio vero che la heap contiene
più spazio allocabile…
Partiamo da:
// test04b1.cpp
int y[1000000000]{254}; // global!
int main()
{
int x[2093000]{254};
return 0;
}
e compiliamo variando la dimensione degli array per vedere dove subentrano problemi.
Poi inseriamo il seguente programma, che riserva un’area di memoria nella heap.
#include <iostream>
using namespace std;
// attention: new does not return 0 on fail!!!
// test04b.cpp
class A {
public:
A(int sz);
~A();
private:
int size;
int *ptr;
};
A::A(int sz)
{
size = sz;
cout << "Constructor: allocating memory\n";
ptr = new int[size];
cout << ptr << endl;
cout << "Constructor: filling memory\n";
for (int j = 0; j < size; j++) *ptr = 32323;
}
A::~A()
{
cout << "Destructor\n";
delete [] ptr;
}
38
{
// int x[2093000]{254};
int sz;
if (argc > 1)
sz = atoi(argv[1]);
else
sz = 100;
cout << "Creating object with " << sz << " integers\n"; //1000000000000
A a(sz);
return 0;
}
https://www.html.it/pag/63405/copia-di-oggetti/
#include <iostream>
using namespace std;
// test05.cpp
class ctest {
private:
int x;
public:
ctest()
{
cout << "Default constructor\n";
x = 0;
}
ctest(int x1)
{
cout << "Constructor by value\n";
x = x1;
}
int getX()
{
return x;
}
void print()
39
{
cout << x << endl;
}
} ;
int main()
{
ctest a;
ctest b(12);
a.print();
b.print();
return 0;
}
Esercizio: modificare la classe in modo che sia conservato un campo in più, il nome della variabile,
e tale campo sia scritto in output dalla print: “La variabile x vale 123”. Il nome sarà assegnato da un
costruttore o tramite una funzione membro set.
https://www.html.it/pag/63405/copia-di-oggetti/
Il costruttore per copia proposto per la classe ctest, non è differente da quello che il compilatore
avrebbe generato per noi. Infatti, la semantica che viene applicata per il costruttore di copia
generato automaticamente è quella della copia membro a membro.
Quando, però, una classe contiene dati membro che sono puntatori o reference, la copia membro a
membro di un oggetto può non essere sufficiente.
In questo caso, si dice che la copia membro a membro è una copia “superficiale” (shallow copy).
Un’istanza della classe ed una sua shallow copy, sono infatti legate, poiché una modifica fatta ad
un dato membro puntatore o reference di una si riflette automaticamente nell’altra, con
conseguenze che possono portare anche all’instabilità del programma.
In questi casi è quindi importante implementare il costruttore di copia correttamente, eliminando
eventuali dipendenze tra un oggetto e le sue copie. Un esempio di questo approccio è mostrato nel
listato seguente
40
class A {
public:
A();
~A();
A(const A& other);
private:
int m;
int *ptr;
};
A a(b);
Esercizio: scrivere una classe A i cui member variable privati siano un int size e un int* ptr; ptr
punti a una zona di memoria allocata con new, la cui lunghezza sia contenuta in size.
La classe preveda:
un costruttore di default, che definisce ptr come null e size=0;
un costruttore con un parametro sz, che allochi sz interi nella heap, e ponga ptr a puntare a
tale zona di memoria, e size = sz; riempia poi la memoria di numeri casuali compresi tra 0 e
99;
un distruttore, che liberi la memoria se era stata allocata (quindi, se ptr non è null);
un costruttore per copia, A::A(const A& other), che copi la size della sorgente nella size della
destinazione, allochi altrettanta memoria, e copi il contenuto della memoria (se di lunghezza
diversa da 0!) nella nuova zona; usare memcpy, presente in <cstring>;
un operatore di assegnazione, A& A::operator=(const A& other); piuttosto complesso: deve
infatti – prima di allocare una nuova zona di memoria da assegnare all’oggetto destinazione
– ricordare di deallocare la vecchia! Poi, se l’oggetto sorgente è non vuoto (quindi size != 0
oppure ptr != null), alloca ed effettua la copia;
una funzione print() che stampa il contenuto della memoria a video (se non vuota); se vuota,
stampa “Empty”
aggiungere, eventualmente, una variabile che conservi il nome della variabile.
Esempio completo
// test06.cpp
#include <iostream>
#include <cstring> // memcpy
#include <cstdlib> // srand, rand http://www.cplusplus.com/reference/cstdlib/rand/?kw=rand
#include <ctime> // time
// attention: new does not return 0 on fail!!! catch exceptions (or use nothrow)!
// http://www.cplusplus.com/reference/new/operator%20new/
class A {
public:
41
A(); // default constructor
A(int sz); // constructor
~A(); // destructor
A(const A& other); // copy constructor
A& operator=(const A& other); // copy constructor
void print();
private:
int size;
int *ptr;
};
A::A()
{
size = 0;
ptr = NULL;
}
A::A(int sz)
{
size = sz;
cout << "Constructor: allocating memory\n";
ptr = new int[size];
cout << ptr << endl;
cout << "Constructor: filling memory\n";
for (int j = 0; j < size; j++) ptr[j] = rand()%100;
}
A::~A()
{
cout << "Destructor\n";
delete [] ptr;
}
size = other.size;
// shallow copy (and memory leak):
// ptr = other.ptr;
// deep copy
if (other.ptr) {
if (ptr) delete [] ptr;
ptr = new int[size];
// http://nadeausoftware.com/articles/2012/05/c_c_tip_how_copy_memory_quickly
if (other.ptr != nullptr)
memcpy( (void*)ptr, (void*) other.ptr, size * sizeof(int) );
}
return *this;
}
void A::print()
{
if (size == 0)
cout << "Empty\n";
else
{
for (int j = 0; j < size; j++)
cout << ptr[j] << ", ";
42
cout << endl;
}
int main()
{
// Seed the pseudo-random number generator
// srand(time(0)); // http://www.cplusplus.com/reference/ctime/time/?kw=time
A a(10);
a.print();
A a1(a);
a1.print();
A a2 = a;
a2.print();
A a3;
a3.print();
a3 = a;
a.print();
A a5(20);
a5.print();
a5 = a;
a5.print();
return 0;
}
Se necessario:
https://nccastaff.bournemouth.ac.uk/jmacey/ASE/labs/lab1.html
Primo test con gnuplot: creare un file, chiamato ad esempio line.dat, contenente le seguenti righe:
43
https://askubuntu.com/questions/342202/failed-to-load-module-canberra-gtk-module-but-already-
installed
Altro esempio:
Generare i punti di una circonferenza con il seguente algoritmo (parametrico: variando il valore di
un parametro t tra 0 e 2 , in corrispondenza generiamo le coordinate del punto corrispondente della
circonferenza):
Chiedi all’utente di inserire il raggio (r) della circonferenza (di tipo double)
Chiedi all’utente di inserire lo step (dt) di incremento del parametro t (di tipo double)
Per t da 0 a 2:
o calcola i corrispondenti valore di x e y, come segue: x=r·cos(t), y=r·sin(t);
o scrivi a video i valori x e y;
o incrementa t della quantità dt: t += dt
I valori emessi a video possono essere rediretti in un file con “>”, per poi essere usati con gnuplot:
attenzione: così non avremo in output alcun messaggio del programma, nemmeno le richieste di
inserire valori: andrà tutto nel file in cui avremo rediretto lo standard output. Per evitare problemi,
possiamo:
mandare in cout i messaggi per l’utente preceduti da #, in modo che poi gnuplot li consideri
commenti.
usare cerr (stderr) e cout (stdout) per mansioni diverse (uno per i messaggi e uno per le
coordinate da redirigere), ma la soluzione non è pulitissima. Ricordare che > redirige (con
troncamento) il cout, mentre 2> redirige il cerr.
In una seconda versione del programma, i dati x e y sono scritti in un file di testo.
In una terza versione, i dati sono scritti in un file di testo, e poi gnuplot è chiamato dall’interno del
codice (vedere di seguito).
Nota: è definito in <cmath> come M_PI; analogamente <cmath> contiene cos e sin.
Esercizio:
E’ possibile tracciare un plot su più colonne; inserire nel file twocol.dat i seguenti dati:
0 0 1
0.01 0.0627905 0.998027
0.02 0.125333 0.992115
Esercizio:
44
Write a program that requests two inputs from the user, one for frequency and one for amplitude,
and then plots both the cos and sine waves for the given values using the algorithm
45
$ command-name &>file
OR
$ command > file-name 2>&1
$ command-name 2>&1
// 2
std::string command2;
command2 = "gnuplot -p ";
command2 += "cossin2.out";
std::system(command2.c_str());
// 3
// note the "’s in the command!
std::string command3 = gnuplot -p -e \"plot 'cossin.out' using 1:2 with lines \";
std::system("command3");
plot "$data" using 1:2 with lines, "$data" using 1:3 with lines
#include "CImg.h"
46
using namespace cimg_library;
int main() {
return 0;
}
Compilare con
g++ -o test01 test01.cpp –I ~/C++/CImg-2.4.2
dove si suppone che il file CImg.h sia in –I ~/C++/CImg-2.4.2 (modificare in base alla
propria configurazione).
#include "CImg.h"
using namespace cimg_library;
47
double offsetY = 0.0;
const int MAX = 255; // maximum number of iterations for mandelbrot()
if (argc > 1) {
zoom = atof(argv[1]);
offsetX = atof(argv[2]);
offsetY = atof(argv[3]);
}
CImgDisplay disp3(image3,"Mandelbrot");
return 0;
while (pow(zReal, 2.0) + pow(zImag, 2.0) <= 4.0 && counter <= maximum) {
nextRe = pow(zReal, 2.0) - pow(zImag, 2.0) + startReal;
zImag = 2.0 * zReal * zImag + startImag;
zReal = nextRe;
if (zReal == startReal && zImag == startImag) { // a repetition indicates that the point
//is in the Mandelbrot set
return -1; // points in the Mandelbrot set are represented by -1
}
counter += 1;
}
E compilare con
g++ -o test01 test01.cpp –I ~/C++/CImg-2.4.2 -O2 -L/usr/X11R6/lib -lm -lpthread -lX11
48
Da wikipedia (https://it.wikipedia.org/wiki/Insieme_di_Mandelbrot):
L'insieme di Mandelbrot o frattale di Mandelbrot è uno dei frattali più popolari. È l'insieme dei
numeri complessi c per i quali la successione definita da:
Ricorsione
http://ictechnologies.altervista.org/matrice-trasposta-c/
49
Library and Include Files
If you have library or include files in non-standard locations, the -L{DIRNAME} and
-I{DIRNAME} options allow you to specify these locations and to insure that they are
searched before the standard locations. For example, if you store custom include files in
/usr/local/include/killerapp, then in order for gcc to find them, your gcc invocation
would be something like
$ gcc someapp.c -I/usr/local/include/killerapp
Similarly, suppose you are testing a new programming library, libnew.so (.so is the
normal extension for shared libraries— more on this subject in Chapter 24, “Using
Libraries”) currently stored in /home/fred/lib, before installing it as a standard system
library. Suppose also that the header files are stored in /home/fred/include.
Accordingly, to link against libnew.so and to help gcc find the header files, your gcc
command line should resemble the following:
$gcc myapp.c -L/home/fred/lib -I/home/fred/include -lnew
The -l option tells the linker to pull in object code from the specified library. In this
example, I wanted to link against libnew.so. A long-standing UNIX convention is that
libraries are named lib{something}, and gcc, like most compilers, relies on this convention.
If you fail to use the -l option when linking against libraries, the link step will fail
and gcc will complain about undefined references to “function_name.”
By default, gcc uses shared libraries, so if you must link against static libraries, you have
to use the -static option. This means that only static libraries will be used. The following
example creates an executable linked against the static ncurses. Chapter 27, “Screen
Manipulation with ncurses,” discusses user interface programming with ncurses:
$ gcc cursesapp.c -lncurses -static
When you link against static libraries, the resulting binary is much larger than using
shared libraries. Why use a static library, then? One common reason is to guarantee that
users can run your program—in the case of shared libraries, the code your program
needs to run is linked dynamically at runtime, rather than statically at compile time. If
the shared library your program requires is not installed on the user’s system, she will get
errors and not be able to run your program.
LIBRERIE OPENCV
http://parliamodi-ubuntu.blogspot.com/p/indice-del-corso-c.html
echo $SHELL
50
[LEZIONE 7]
File binari
Sia i file binari che quelli testuali sono sequenze di byte (=char) salvati su disco.
In un file testuale, sia le stringhe sia i numeri sono sequenze di caratteri:
ciao 1234 è salvato come la sequenza di “c”, “i”, “a”, “o”, “ ”, “1”, “2”, “3”, “4”
In un file binario, le stringhe sono sequenze di caratteri, ma i numeri sono usualmente salvati nella
codifica del tipo del numero stesso; per esempio il numero intero 1234 è conservato come 04D2
(esadecimale) che, in 4 byte (int) appare come D2, 04, 0, 0 (in codifica little endian, ossia byte
meno significativi prima, in contrapposizione a big endian [usato nel formato pgm, vedere di
seguito]), ovvero in decimale 210, 4, 0, 0. Sono sempre 4 byte… ma 1234567890 è codificato come
499602D2, ossia D2, 02, 96, 49, ancora 4 byte invece di 10 (nel caso di “una cifra = un byte”).
ciao 1234 è salvato come la sequenza di “c”, “i”, “a”, “o”, “ ”, 210, 4, 0, 0
I caratteri 210, 4, 0, 0 appaiono, se visti “come testo”, in base al loro corrispondente significato nel
codice ASCII:
(da http://www.thealmightyguru.com/Pointless/ASCII.html)
51
Nei file testuali usualmente i fine-riga sono marcati da caratteri speciali : CR (dec 13) o LF (dec 10)
o entrambi a seconda del sistema operativo (quindi i file non hanno la medesima lunghezza se creati
in SO diversi).
Nei file binari il formato è rigido (o comunque può esserlo) perché i dati numerici occupano spazi
fissi dipendenti dal tipo di dato. Nei file testuali la lunghezza di un numero dipende da quante cifre
esso ha in decimale.
https://askubuntu.com/questions/19479/what-are-some-good-gui-binary-viewers-editors
sudo apt install ghex (editor binario)
#include <iostream>
#include <fstream>
#include <cstring>
int main()
{
Cambiare in:
int x = 256;
f.write((char *) &x, sizeof(int));
http://netpbm.sourceforge.net/doc/pgm.html
1. A "magic number" for identifying the file type. A pgm image's magic number is the two characters "P5".
2. Whitespace (blanks, TABs, CRs, LFs).
3. A width, formatted as ASCII characters in decimal.
4. Whitespace.
5. A height, again in ASCII decimal.
6. Whitespace.
7. The maximum gray value (Maxval), again in ASCII decimal. Must be less than 65536, and more than zero.
8. A single whitespace character (usually a newline).
9. A raster of Height rows, in order from top to bottom. Each row consists of Width gray values, in order from left to right. Each gray value
is a number from 0 through Maxval, with 0 being black and Maxval being white. Each gray value is represented in pure binary by either
1 or 2 bytes. If the Maxval is less than 256, it is 1 byte. Otherwise, it is 2 bytes. The most significant byte is first.
A row of an image is horizontal. A column is vertical. The pixels in the image are square and contiguous.
All characters referred to herein are encoded in ASCII. "newline" refers to the character known in ASCII as Line Feed or LF. A "white space"
character is space, CR, LF, TAB, VT, or FF (I.e. what the ANSI standard C isspace() function calls white space).
52
Plain PGM
There is actually another version of the PGM format that is fairly rare: "plain" PGM format. The format above, which generally considered the normal
one, is known as the "raw" PGM format. See pbm for some commentary on how plain and raw formats relate to one another and how to use them.
……
int main()
{
// Write the pgm header
//
Come codificare una matrice bidimensionale in C++ senza usare librerie apposite (quale boost)
// 2D arrays (matrices)
#include <vector>
int main() {
int rows = 100, cols = 200;
int r = 20, c = 35;
////////////////////////////
// DYNAMIC ALLOCATION
////////////////////////////
// http://www.cplusplus.com/doc/tutorial/dynamic/ (new; exceptions)
// https://stackoverflow.com/questions/1403150/how-do-you-dynamically-allocate-a-matrix
matrixA[r][c] = 135;
53
// matrixA[r][c]= matrixA1[r][c];
////////////////////////////
matrixA[r][c] = 135;
delete[] matrixB[0];
delete[] matrixB;
////////////////////////////
matrixC[idx] = 135;
// Copy
// for(int i=0;i < rows*cols;++i)
// matrixC[i]=matrixC1[i];
delete[] matrixC;
////////////////////////////////////////
// USE OF (STANDARD LIBRARY) VECTOR TYPE
////////////////////////////////////////
std::vector<std::vector<int>> matrixD;
matrixD[r][c] = 135;
}
// also:
// std::vector< std::vector<int> > matrixD (rows, std::vector<int>(cols,0));
// correct even if cols = 0, rows = 0;
class ctest
{
public:
54
static int a; // dichiarazione
int b;
public:
void impostaDati(int a, int b)
{
this -> a = a;
this -> b = b;
}
} ;
int main()
{
ctest t1;
ctest t2;
t1.impostaDati(10, 20);
cout << t1.a << endl;
cout << t1.b << endl;
t2.impostaDati(100, 200);
cout << t2.a << endl;
cout << t2.b << endl;
return 0;
}
class ctest
{
private:
static int a; // dichiarazione
int b;
public:
void impostaDati(int a, int b)
{
this -> a = a;
this -> b = b;
}
void stampaDati()
{
cout << a << endl;
cout << b << endl;
}
} ;
int main()
{
ctest t1;
ctest t2;
55
t1.impostaDati(10, 20);
t1.stampaDati();
t2.impostaDati(100, 200);
t2.stampaDati();
t1.stampaDati();
return 0;
}
Registrare variabili comuni a tutti gli oggetti di una classe, oppure uso come contatore!
[LEZIONE 8]
#include <iostream>
using namespace std;
class A {
private:
int x;
public:
A() {
x = 0;
cout << "Default Constructor\n";
}
A(int x) {
this->x = x;
cout << "Constructor\n";
}
A(const A &acopied) {
cout << "Copy Constructor\n";
x = acopied.x;
}
int main() {
56
A aa;
A a(22), b(33);
// a.x = 22;
a.print();
b.print();
aa.print();
A aaa(a);
aaa.print();
aaa = b;
aaa.print();
aaa.setX(123);
aaa.print();
cout << aaa.getX() << endl;
A aaaa(b);
A aaaaa = aaaa;
A d(A(55)); // <--
return 0;
}
Overloading di funzioni
https://www.learncpp.com/cpp-tutorial/76-function-overloading/
Overloading di operatori
https://www.learncpp.com/cpp-tutorial/91-introduction-to-operator-overloading/
https://www.learncpp.com/cpp-tutorial/813-friend-functions-and-classes/
è possibile leggere l’uso delle funzioni friend per l’overloading degli operatori:
https://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators-using-friend-functions/
https://www.learncpp.com/cpp-tutorial/9-2a-overloading-operators-using-normal-functions/
https://www.learncpp.com/cpp-tutorial/94-overloading-operators-using-member-functions/
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
57
vector<double> operator+(const vector<double> &v1, const vector<double> &v2)
{
if (v1.size() != v2.size())
// http://www.cplusplus.com/reference/cmath/nan-function/
return vector<double>(1, nan(""));
else
{
vector<double> v(v1.size());
for (int j = 0; j < v1.size(); j++)
v[j] = v1[j] + v2[j];
return v;
}
}
int main()
{
v1.push_back(10);
v1.push_back(20);
v2.push_back(100);
v2.push_back(200);
vector<double> v;
v = v1 + v2;
cout << v[0] << " " << v[1] << endl;
cout << v << endl;
vector<double> v3;
v3.push_back(10);
v3.push_back(20);
cout << (v1 == v3) << endl;
return 0;
}
Esercizio
58
Scrivere un programma contenente una classe, denominate cimage, che modelli il tipo “immagine
bidimensionale a 8 bit” (toni di grigio, unsigned int 8 bit, quindi unsigned char sui sistemi più diffusi), di
dimensioni arbitrarie (per numero righe e numero colonne).
Siano implementati:
Il programma poi dichiari un’immagine, disegni dei pixel, salvi come pgm. Si visualizzi con eog (o altro
strumento) per constatare il risultato.
[LEZIONE 9]
#include <iostream>
using namespace std;
class A {
private:
int x;
public:
A() {
x = 0;
59
cout << "Default Constructor\n";
}
A(int x) {
this->x = x;
cout << "Constructor\n";
}
A(const A &acopied) {
cout << "Copy Constructor\n";
x = acopied.x;
}
int getX() const { return x; } // <---- const!!! otherwise errors when getting from const a
};
int main() {
A aa;
A a(22), b(33);
// a.x = 22;
a.print();
b.print();
aa.print();
A aaa(a);
aaa.print();
aaa = b;
aaa.print();
aaa.setX(123);
aaa.print();
60
cout << aaa.getX() << endl;
A aaaa(b);
A aaaaa = aaaa;
A d(A(55)); // <--
//
aa = a + b;
aa.print();
return 0;
}
Ereditarietà (inheritance)
https://www.learncpp.com/cpp-tutorial/111-introduction-to-inheritance/
#include <iostream>
#include <string>
class Person
{
private:
std::string m_name;
int m_age;
public:
// constructor with default parameters
Person(std::string name = "", int age = 0) : m_name(name), m_age(age)
{
std::cout << "constructor for: Person\n";
}
};
int main()
61
{
Person p1("Pippo", 35);
std::cout << p1.getName() << std::endl;
std::cout << p1.getAge() << std::endl;
BaseballPlayer b1(5.5, 12);
std::cout << b1.getName() << std::endl;
std::cout << b1.getAge() << std::endl;
std::cout << b1.getBattingAverage () << std::endl;
std::cout << b1.getHomeRuns () << std::endl;
return 0;
}
#include <iostream>
#include <string>
class Person
{
private:
std::string m_name;
int m_age;
public:
// constructor with default parameters
Person(std::string name = "", int age = 0) : m_name(name), m_age(age)
{
std::cout << "constructor for: Person\n";
}
};
62
int main()
{
Person p1("Pippo", 35);
std::cout << p1.getName() << std::endl;
std::cout << p1.getAge() << std::endl;
BaseballPlayer b1("Topolino", 38, 5.5, 12);
std::cout << b1.getName() << std::endl;
std::cout << b1.getAge() << std::endl;
std::cout << b1.getBattingAverage () << std::endl;
std::cout << b1.getHomeRuns () << std::endl;
return 0;
}
https://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/
Overriding
https://www.learncpp.com/cpp-tutorial/11-6a-calling-inherited-functions-and-overriding-behavior/
Esercizio no.1
Realizza una classe 'contatore' denominata C che contenga una variabile intera 'valore' come attributo e tre
metodi, il primo sia un costruttore senza parametri che inizializzi 'valore' a 0; il secondo incrementi 'valore '
di 1 tutte le volte che è invocato; il terzo stampi a video il valore della variabile 'valore'.
Esegui il test del programma con un main che definisca un oggetto della classe contatore, e poi ne
incrementi il valore in un ciclo for, via via stampandolo a video.
Arricchire il programma derivando, dalla classe C, una sottoclasse "contatore colorato" denominata
ColouredC, che abbia in piu' un colore che cicli tra quelli fondamentali, per esempio:
63
incrementare un ColouredC significa sia aumentare di 1 il valore, sia ciclare tra i colori (red->orange->…-
>violet->red…)
- una classe denominata "CProduct" che rappresenti un tipo di prodotto alimentare e abbia i seguenti data
members privati:
name (di tipo string; valori possibili "pasta", "patata", "olio", "passataDiPomodoro",
"formaggioParmigiano")
proteins (double; contenuto in termini di proteine, per 100 g)
carbohydrates (double; contenuto in termini di carboidrati, per 100 g)
lypids (double; contenuto in termini di grassi, per 100 g)
un costruttore di default
un costruttore con inizializzazione dei data members
quattro funzioni di tipo get e di tipo set, rispettivamente per leggere e restituire i quattro data
members, e per impostarne il valore
una funzione di print che scriva a video le informazioni sul prodotto
altri che si giudicasse necessario inserire.
- una classe CProductPortion derivata da CProduct che rappresenti una porzione del prodotto, e quindi
abbia in piu' solo un data member privato:
e i function members necessari, in particolare un paio di costruttori (default e con inizializzazione) getter,
setter, print...
La classe CProductPortion abbia inoltre un function member che calcoli e restituisca in kcal il valore
energetico della porzione, considerato che
1g di proteine dà 4 kcal / g
1g di grassi dà 9 kcal / g
1g di carboidrati dà 4 kcal / g
3) Nel main, si definiscano alcune variabili chiamate pane, pasta, etc di tipo CProduct contenenti i prodotti
seguenti:
Infine (ouff!) sempre nel main si dichiari un vettore di CProductPortion denominato spaghettata che abbia
al suo interno, come elementi:
Ciascuno di questi elementi sarà stato definito a partire dagli alimenti pane, pasta etc di cui si è parlato più
sopra.
[Lezione 10]
Metodi virtuali
https://www.learncpp.com/cpp-tutorial/121-pointers-and-references-to-the-base-class-of-derived-objects/
65
https://www.learncpp.com/cpp-tutorial/122-virtual-functions/
#include <iostream>
#include <string>
using namespace std;
class A {
protected:
string name;
public:
A(string n = "") : name(n)
{
cout << "A constructor\n";
}
virtual void whoAmI()
{
cout << "I am an A and my name is " << name << endl;
}
} ;
class B : public A
{
public:
B(string n = "") : A(n)
{
cout << "B constructor\n";
}
virtual void whoAmI()
{
cout << "I am an B and my name is " << name << endl;
}
} ;
int main()
{
A a("Pippo");
a.whoAmI();
cout << endl;
B b("Minnie");
b.whoAmI();
cout << endl;
A& rA = a;
rA.whoAmI();
cout << endl;
B& rB = b;
rB.whoAmI();
cout << endl;
return 0;
66
}
clang-format -I main.cc
https://www.geeksforgeeks.org/const-member-functions-c/
#include<iostream>
using namespace std;
class Test {
int value;
public:
Test(int v = 0) {value = v;}
int main() {
Test t(20);
cout<<t.getValue();
return 0;
}
67
Output:
20
When a function is declared as const, it can be called on any type of object. Non-const functions can only be called by non-const objects.
For example the following program has compiler errors.
#include<iostream>
using namespace std;
class Test {
int value;
public:
Test(int v = 0) {value = v;}
int getValue() {return value;}
};
int main() {
const Test t;
cout << t.getValue();
return 0;
}
Output:
passing 'const Test' as 'this' argument of 'int
Test::getValue()' discards qualifiers
68