Calcolatrice Programma C++ Avanzata
Guida Completa alla Creazione di una Calcolatrice in C++
La creazione di una calcolatrice in C++ è un progetto fondamentale per comprendere i concetti base della programmazione come input/output, operatori aritmetici, strutture di controllo e gestione degli errori. Questa guida approfondita ti condurrà attraverso tutti gli aspetti necessari per sviluppare una calcolatrice avanzata in C++, con esempi pratici e best practice.
1. Fondamenti della Calcolatrice in C++
Una calcolatrice in C++ può essere implementata in diversi modi, a seconda della complessità richiesta:
- Calcolatrice base: Operazioni aritmetiche semplici (addizione, sottrazione, moltiplicazione, divisione)
- Calcolatrice scientifica: Funzioni trigonometriche, logaritmi, esponenziali
- Calcolatrice a oggetti: Implementazione con programmazione orientata agli oggetti
- Calcolatrice con interfaccia: Utilizzo di librerie grafiche come Qt o GTK
2. Struttura di Base di una Calcolatrice C++
Ecco la struttura fondamentale di una semplice calcolatrice in C++:
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main() {
char op;
double num1, num2;
cout << "Inserisci operatore (+, -, *, /): ";
cin >> op;
cout << "Inserisci due numeri: ";
cin >> num1 >> num2;
switch(op) {
case '+':
cout << num1 + num2;
break;
case '-':
cout << num1 - num2;
break;
case '*':
cout << num1 * num2;
break;
case '/':
if(num2 != 0)
cout << num1 / num2;
else
cout << "Errore: Divisione per zero!";
break;
default:
cout << "Operatore non valido!";
}
return 0;
}
3. Gestione degli Errori
Una calcolatrice robusta deve gestire diversi tipi di errori:
- Divisione per zero: Il caso più comune che può causare crash del programma
- Input non validi: Gestione di input che non sono numeri
- Overflow: Quando il risultato supera i limiti del tipo di dato
- Underflow: Quando il risultato è troppo piccolo per essere rappresentato
Ecco un esempio di gestione avanzata degli errori:
#include <iostream>
#include <limits>
#include <cmath>
using namespace std;
int main() {
char op;
double num1, num2;
try {
cout << "Inserisci operatore (+, -, *, /): ";
if (!(cin >> op)) {
throw runtime_error("Input operatore non valido");
}
cout << "Inserisci due numeri: ";
if (!(cin >> num1 >> num2)) {
throw runtime_error("Input numerico non valido");
}
switch(op) {
case '+':
if ((num1 > 0 && num2 > numeric_limits<double>::max() - num1) ||
(num1 < 0 && num2 < numeric_limits<double>::lowest() - num1)) {
throw overflow_error("Overflow nell'addizione");
}
cout << "Risultato: " << num1 + num2;
break;
// ... altre operazioni con gestione errori simile
}
} catch(const exception& e) {
cerr << "Errore: " << e.what() << endl;
return 1;
}
return 0;
}
4. Implementazione con Classi (OOP)
Una versione orientata agli oggetti offre maggiore flessibilità e riutilizzo del codice:
#include <iostream>
#include <stdexcept>
class Calculator {
private:
double result;
public:
Calculator() : result(0) {}
void add(double a, double b) {
result = a + b;
}
void subtract(double a, double b) {
result = a - b;
}
void multiply(double a, double b) {
result = a * b;
}
void divide(double a, double b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
result = a / b;
}
double getResult() const {
return result;
}
};
int main() {
Calculator calc;
char op;
double num1, num2;
std::cout << "Enter operator (+, -, *, /): ";
std::cin >> op;
std::cout << "Enter two numbers: ";
std::cin >> num1 >> num2;
try {
switch(op) {
case '+': calc.add(num1, num2); break;
case '-': calc.subtract(num1, num2); break;
case '*': calc.multiply(num1, num2); break;
case '/': calc.divide(num1, num2); break;
default: throw std::runtime_error("Invalid operator");
}
std::cout << "Result: " << calc.getResult() << std::endl;
} catch(const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
5. Calcolatrice Scientifica Avanzata
Per implementare funzioni scientifiche, possiamo utilizzare la libreria <cmath>:
| Funzione | Descrizione | Esempio C++ |
|---|---|---|
| sin(x) | Seno di x (x in radianti) | double y = sin(1.5708); // ≈ 1.0 |
| cos(x) | Coseno di x (x in radianti) | double y = cos(3.14159); // ≈ -1.0 |
| tan(x) | Tangente di x (x in radianti) | double y = tan(0.7854); // ≈ 1.0 |
| exp(x) | Funzione esponenziale (e^x) | double y = exp(1); // ≈ 2.718 |
| log(x) | Logaritmo naturale (base e) | double y = log(2.718); // ≈ 1.0 |
| log10(x) | Logaritmo base 10 | double y = log10(100); // = 2.0 |
| pow(x,y) | x elevato alla y | double y = pow(2, 3); // = 8.0 |
| sqrt(x) | Radice quadrata di x | double y = sqrt(16); // = 4.0 |
6. Ottimizzazione delle Prestazioni
Per calcolatrici che devono eseguire molte operazioni, è importante considerare l’ottimizzazione:
- Evita calcoli ridondanti: Memorizza risultati intermedi quando possibile
- Usa i tipi di dato appropriati: int per numeri interi, double per precisione decimale
- Considera l’inlining: Per funzioni piccole e frequentemente chiamate
- Ottimizza i loop: Riducine il numero quando possibile
- Usa costanti esplicite: Per valori che non cambiano (es. const double PI = 3.14159)
Esempio di ottimizzazione con template:
template<typename T>
T fast_add(T a, T b) {
return a + b;
}
// Specializzazione per float
template<>
float fast_add<float>(float a, float b) {
// Implementazione ottimizzata per float
return a + b;
}
7. Testing e Debugging
Il testing è cruciale per una calcolatrice affidabile. Ecco alcune strategie:
- Test unitari: Testa ogni funzione individualmente
- Test di edge case: Valori massimi/minimi, divisione per zero
- Test di precisione: Verifica che i risultati siano accurati
- Test di performance: Misura i tempi di esecuzione
- Debugging: Usa strumenti come GDB o output di debug
Esempio di framework di test semplice:
#include <iostream>
#include <cassert>
#include <cmath>
void test_addition() {
assert(5 + 3 == 8);
assert(0 + 0 == 0);
assert(-1 + 1 == 0);
assert(1.5 + 2.5 == 4.0);
std::cout << "Addition tests passed!\n";
}
void test_division() {
assert(10 / 2 == 5);
assert(9.0 / 3 == 3.0);
// Test per divisione per zero dovrebbe essere gestito separatamente
std::cout << "Division tests passed!\n";
}
int main() {
test_addition();
test_division();
// Altri test...
return 0;
}
8. Interfaccia Utente Avanzata
Per creare un’interfaccia grafica, possiamo usare librerie come:
- Qt: Framework completo per interfacce grafiche
- GTK: Toolkit per interfacce grafiche
- FLTK: Leggero e semplice da usare
- ImGui: Per interfacce immediate (immediate mode GUI)
Esempio con Qt:
#include <QApplication>
#include <QPushButton>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QLabel>
class Calculator : public QWidget {
Q_OBJECT
public:
Calculator(QWidget *parent = nullptr) {
QVBoxLayout *layout = new QVBoxLayout(this);
display = new QLineEdit("0");
display->setReadOnly(true);
layout->addWidget(display);
// Aggiungi pulsanti per 0-9, +, -, *, /, =, etc.
for (int i = 0; i < 10; ++i) {
QPushButton *btn = new QPushButton(QString::number(i));
connect(btn, &QPushButton::clicked, this, &Calculator::digitClicked);
layout->addWidget(btn);
}
// ... altri pulsanti e connessioni
}
public slots:
void digitClicked() {
QPushButton *btn = qobject_cast<QPushButton*>(sender());
// Logica per gestire il click sul pulsante
}
private:
QLineEdit *display;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Calculator calc;
calc.show();
return app.exec();
}
9. Calcolatrice con Memoria
Implementare una funzione di memoria aggiunge utilità alla calcolatrice:
#include <iostream>
#include <vector>
class MemoryCalculator {
private:
double memory;
std::vector<double> history;
public:
MemoryCalculator() : memory(0) {}
void addToMemory(double value) {
memory += value;
}
void clearMemory() {
memory = 0;
}
double recallMemory() const {
return memory;
}
void store(double value) {
history.push_back(value);
}
void showHistory() const {
std::cout << "History:\n";
for (double val : history) {
std::cout << val << "\n";
}
}
// ... altre funzioni della calcolatrice
};
10. Calcolatrice con Espressioni
Per valutare espressioni matematiche complesse, possiamo:
- Implementare un parser semplice
- Usare la notazione polacca inversa (RPN)
- Utilizzare librerie esistenti come muParser
- Implementare l’algoritmo Shunting-yard
Esempio semplice con valutazione di espressioni:
#include <iostream>
#include <string>
#include <stack>
#include <cctype>
#include <cmath>
#include <stdexcept>
double evaluateExpression(const std::string& expr) {
std::stack<double> values;
std::stack<char> ops;
for (size_t i = 0; i < expr.length(); i++) {
if (expr[i] == ' ') continue;
if (expr[i] == '(') {
ops.push(expr[i]);
}
else if (isdigit(expr[i])) {
double val = 0;
while (i < expr.length() && isdigit(expr[i])) {
val = (val * 10) + (expr[i] - '0');
i++;
}
if (i < expr.length() && expr[i] == '.') {
i++;
double fraction = 1;
while (i < expr.length() && isdigit(expr[i])) {
fraction /= 10;
val += (expr[i] - '0') * fraction;
i++;
}
}
values.push(val);
i--;
}
else if (expr[i] == ')') {
while (!ops.empty() && ops.top() != '(') {
double val2 = values.top(); values.pop();
double val1 = values.top(); values.pop();
char op = ops.top(); ops.pop();
switch(op) {
case '+': values.push(val1 + val2); break;
case '-': values.push(val1 - val2); break;
case '*': values.push(val1 * val2); break;
case '/': values.push(val1 / val2); break;
case '^': values.push(pow(val1, val2)); break;
}
}
if (!ops.empty()) ops.pop(); // Pop the '('
}
else {
while (!ops.empty() && ops.top() != '(') {
double val2 = values.top(); values.pop();
double val1 = values.top(); values.pop();
char op = ops.top(); ops.pop();
switch(op) {
case '+': values.push(val1 + val2); break;
case '-': values.push(val1 - val2); break;
case '*': values.push(val1 * val2); break;
case '/': values.push(val1 / val2); break;
case '^': values.push(pow(val1, val2)); break;
}
}
ops.push(expr[i]);
}
}
while (!ops.empty()) {
double val2 = values.top(); values.pop();
double val1 = values.top(); values.pop();
char op = ops.top(); ops.pop();
switch(op) {
case '+': values.push(val1 + val2); break;
case '-': values.push(val1 - val2); break;
case '*': values.push(val1 * val2); break;
case '/': values.push(val1 / val2); break;
case '^': values.push(pow(val1, val2)); break;
}
}
return values.top();
}
int main() {
std::string expr = "10 + 2 * (6 - 2)";
std::cout << expr << " = " << evaluateExpression(expr) << std::endl;
return 0;
}
11. Confronto tra Implementazioni
| Caratteristica | Implementazione Base | Implementazione OOP | Implementazione con GUI | Implementazione Scientifica |
|---|---|---|---|---|
| Complessità del codice | Bassa | Media | Alta | Molto Alta |
| Riutilizzo del codice | Basso | Alto | Medium | Alto |
| Prestazioni | Ottime | Buone | Medie | Variabili |
| Funzionalità | Base | Media | Avanzata | Completa |
| Manutenibilità | Bassa | Alta | Media | Alta |
| Tempo di sviluppo | Breve | Medium | Lungo | Molto Lungo |
12. Best Practice per Calcolatrici in C++
- Gestione degli errori: Sempre validare gli input e gestire le eccezioni
- Documentazione: Commenta il codice e fornisci documentazione per le funzioni
- Testing: Crea test completi per tutte le funzionalità
- Modularità: Dividi il codice in funzioni e classi logiche
- Prestazioni: Ottimizza le parti critiche del codice
- Portabilità: Evita codice specifico per piattaforma quando possibile
- Sicurezza: Proteggi da buffer overflow e altri problemi di sicurezza
- Interfaccia utente: Rendi l’interfaccia intuitiva e user-friendly
- Gestione della memoria: Evita memory leak (usa smart pointer in C++ moderno)
- Standard moderni: Usa C++11/14/17/20 quando possibile
13. Esempio Completo: Calcolatrice Scientifica
Ecco un esempio completo di calcolatrice scientifica in C++ con molte delle funzionalità discusse:
#include <iostream>
#include <cmath>
#include <iomanip>
#include <limits>
#include <stdexcept>
#include <vector>
#include <memory>
class ScientificCalculator {
private:
double memory;
std::vector<double> history;
const double PI = 3.14159265358979323846;
const double E = 2.71828182845904523536;
public:
ScientificCalculator() : memory(0) {}
// Operazioni di base
double add(double a, double b) {
double result = a + b;
history.push_back(result);
return result;
}
double subtract(double a, double b) {
double result = a - b;
history.push_back(result);
return result;
}
double multiply(double a, double b) {
double result = a * b;
history.push_back(result);
return result;
}
double divide(double a, double b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
double result = a / b;
history.push_back(result);
return result;
}
// Funzioni scientifiche
double squareRoot(double x) {
if (x < 0) {
throw std::runtime_error("Square root of negative number");
}
double result = std::sqrt(x);
history.push_back(result);
return result;
}
double power(double base, double exponent) {
double result = std::pow(base, exponent);
history.push_back(result);
return result;
}
double sine(double x, bool degrees = false) {
double radians = degrees ? x * PI / 180.0 : x;
double result = std::sin(radians);
history.push_back(result);
return result;
}
double cosine(double x, bool degrees = false) {
double radians = degrees ? x * PI / 180.0 : x;
double result = std::cos(radians);
history.push_back(result);
return result;
}
double tangent(double x, bool degrees = false) {
double radians = degrees ? x * PI / 180.0 : x;
double result = std::tan(radians);
history.push_back(result);
return result;
}
double logarithm(double x, double base = 10.0) {
if (x <= 0 || base <= 0 || base == 1) {
throw std::runtime_error("Invalid logarithm arguments");
}
double result = std::log(x) / std::log(base);
history.push_back(result);
return result;
}
double naturalLog(double x) {
if (x <= 0) {
throw std::runtime_error("Natural log of non-positive number");
}
double result = std::log(x);
history.push_back(result);
return result;
}
double exponential(double x) {
double result = std::exp(x);
history.push_back(result);
return result;
}
// Funzioni di memoria
void addToMemory(double value) {
memory += value;
}
void clearMemory() {
memory = 0;
}
double recallMemory() const {
return memory;
}
// Funzioni di storia
void showHistory() const {
std::cout << "Calculation History:\n";
for (size_t i = 0; i < history.size(); ++i) {
std::cout << i + 1 << ": " << history[i] << "\n";
}
}
void clearHistory() {
history.clear();
}
// Costanti
double getPi() const { return PI; }
double getE() const { return E; }
};
int main() {
ScientificCalculator calc;
char choice;
double num1, num2;
std::cout << "Scientific Calculator\n";
std::cout << "-------------------\n";
do {
std::cout << "\nMenu:\n";
std::cout << "1. Addizione\n";
std::cout << "2. Sottrazione\n";
std::cout << "3. Moltiplicazione\n";
std::cout << "4. Divisione\n";
std::cout << "5. Radice quadrata\n";
std::cout << "6. Potenza\n";
std::cout << "7. Seno\n";
std::cout << "8. Coseno\n";
std::cout << "9. Tangente\n";
std::cout << "A. Logaritmo\n";
std::cout << "B. Logaritmo naturale\n";
std::cout << "C. Esponenziale\n";
std::cout << "D. Costanti (π, e)\n";
std::cout << "M. Operazioni memoria\n";
std::cout << "H. Storia calcoli\n";
std::cout << "Q. Esci\n";
std::cout << "Scelta: ";
std::cin >> choice;
try {
switch (toupper(choice)) {
case '1':
std::cout << "Inserisci due numeri: ";
std::cin >> num1 >> num2;
std::cout << "Risultato: " << calc.add(num1, num2) << "\n";
break;
case '2':
std::cout << "Inserisci due numeri: ";
std::cin >> num1 >> num2;
std::cout << "Risultato: " << calc.subtract(num1, num2) << "\n";
break;
case '3':
std::cout << "Inserisci due numeri: ";
std::cin >> num1 >> num2;
std::cout << "Risultato: " << calc.multiply(num1, num2) << "\n";
break;
case '4':
std::cout << "Inserisci due numeri: ";
std::cin >> num1 >> num2;
std::cout << "Risultato: " << calc.divide(num1, num2) << "\n";
break;
case '5':
std::cout << "Inserisci un numero: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.squareRoot(num1) << "\n";
break;
case '6':
std::cout << "Inserisci base ed esponente: ";
std::cin >> num1 >> num2;
std::cout << "Risultato: " << calc.power(num1, num2) << "\n";
break;
case '7': {
char unit;
std::cout << "Inserisci angolo in (R)adianti o (D)egrees: ";
std::cin >> unit;
std::cout << "Inserisci angolo: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.sine(num1, toupper(unit) == 'D') << "\n";
break;
}
case '8': {
char unit;
std::cout << "Inserisci angolo in (R)adianti o (D)egrees: ";
std::cin >> unit;
std::cout << "Inserisci angolo: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.cosine(num1, toupper(unit) == 'D') << "\n";
break;
}
case '9': {
char unit;
std::cout << "Inserisci angolo in (R)adianti o (D)egrees: ";
std::cin >> unit;
std::cout << "Inserisci angolo: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.tangent(num1, toupper(unit) == 'D') << "\n";
break;
}
case 'A':
std::cout << "Inserisci numero e base (default 10): ";
std::cin >> num1;
std::cout << "Inserisci base (premi 0 per default): ";
std::cin >> num2;
if (num2 == 0) num2 = 10;
std::cout << "Risultato: " << calc.logarithm(num1, num2) << "\n";
break;
case 'B':
std::cout << "Inserisci numero: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.naturalLog(num1) << "\n";
break;
case 'C':
std::cout << "Inserisci esponente: ";
std::cin >> num1;
std::cout << "Risultato: " << calc.exponential(num1) << "\n";
break;
case 'D':
std::cout << "Costanti:\n";
std::cout << "π = " << calc.getPi() << "\n";
std::cout << "e = " << calc.getE() << "\n";
break;
case 'M': {
char memChoice;
std::cout << "Operazioni memoria:\n";
std::cout << "1. Aggiungi a memoria\n";
std::cout << "2. Sottrai da memoria\n";
std::cout << "3. Richiamare memoria\n";
std::cout << "4. Azzera memoria\n";
std::cout << "Scelta: ";
std::cin >> memChoice;
switch (memChoice) {
case '1':
std::cout << "Valore da aggiungere: ";
std::cin >> num1;
calc.addToMemory(num1);
std::cout << "Memoria aggiornata. Totale: " << calc.recallMemory() << "\n";
break;
case '2':
std::cout << "Valore da sottrarre: ";
std::cin >> num1;
calc.addToMemory(-num1);
std::cout << "Memoria aggiornata. Totale: " << calc.recallMemory() << "\n";
break;
case '3':
std::cout << "Valore in memoria: " << calc.recallMemory() << "\n";
break;
case '4':
calc.clearMemory();
std::cout << "Memoria azzerata\n";
break;
}
break;
}
case 'H':
calc.showHistory();
break;
case 'Q':
std::cout << "Uscita...\n";
break;
default:
std::cout << "Scelta non valida!\n";
}
} catch (const std::exception& e) {
std::cerr << "Errore: " << e.what() << "\n";
}
} while (toupper(choice) != 'Q');
return 0;
}
14. Ottimizzazione per Prestazioni
Per calcolatrici che devono eseguire molti calcoli, considerare queste ottimizzazioni:
- Evita calcoli ridondanti: Memorizza risultati intermedi
- Usa tipi di dato appropriati: float per precisione single, double per double
- Ottimizza i loop: Riducine il numero e la complessità
- Usa funzioni inline: Per funzioni piccole e frequenti
- Evita allocazioni dinamiche: Quando possibile, usa stack allocation
- Usa costanti esplicite: Per valori che non cambiano
- Considera SIMD: Per operazioni vettoriali (SSE, AVX)
- Profiling: Usa strumenti come gprof per identificare bottleneck
Esempio di ottimizzazione con template e constexpr:
template<typename T>
constexpr T fast_pow(T base, unsigned int exp) {
T result = 1;
while (exp) {
if (exp & 1) {
result *= base;
}
base *= base;
exp >>= 1;
}
return result;
}
// Uso:
constexpr double square = fast_pow(2.0, 2); // Calcolato a compile-time
15. Testing Automatico
Implementare test automatici è cruciale per la qualità del software:
#include <cassert>
#include <cmath>
void test_calculator() {
ScientificCalculator calc;
// Test operazioni di base
assert(fabs(calc.add(2, 3) - 5) < 1e-9);
assert(fabs(calc.subtract(5, 3) - 2) < 1e-9);
assert(fabs(calc.multiply(2, 3) - 6) < 1e-9);
assert(fabs(calc.divide(6, 3) - 2) < 1e-9);
// Test funzioni scientifiche
assert(fabs(calc.squareRoot(9) - 3) < 1e-9);
assert(fabs(calc.power(2, 3) - 8) < 1e-9);
assert(fabs(calc.sine(0) - 0) < 1e-9);
assert(fabs(calc.cosine(0) - 1) < 1e-9);
// Test memoria
calc.addToMemory(5);
calc.addToMemory(3);
assert(fabs(calc.recallMemory() - 8) < 1e-9);
std::cout << "Tutti i test passati!\n";
}
int main() {
test_calculator();
// ... resto del programma
}
16. Estensioni Avanzate
Per progetti più avanzati, considerare:
- Calcolo simbolico: Manipolazione di espressioni simboliche
- Supporto per numeri complessi: Usando std::complex
- Calcolo matriciale: Operazioni su matrici
- Interfaccia grafica: Con Qt o altre librerie
- Calcolo parallelo: Per operazioni intensive
- Integrazione con altre librerie: Come Eigen per algebra lineare
- Supporto per script: Permettere all’utente di salvare sequenze di operazioni
- Interfaccia a linea di comando avanzata: Con completamento automatico
17. Sicurezza
Considerazioni di sicurezza per una calcolatrice in C++:
- Buffer overflow: Evita con l’uso di container STL invece di array C-style
- Division by zero: Sempre gestire questo caso
- Input validation: Verifica sempre gli input dell’utente
- Memory safety: Usa smart pointer invece di raw pointer quando possibile
- Type safety: Evita cast non sicuri
- Error handling: Usa eccezioni o codici di errore consistenti
- Concurrency: Se usi thread, assicurati della thread safety
18. Portabilità
Per massimizzare la portabilità del codice:
- Evita codice specifico per piattaforma: Quando possibile
- Usa standard C++: Evita estensioni del compilatore
- Gestisci differenze di endianness: Se lavori con dati binari
- Considera le differenze di floating-point: Tra diverse architetture
- Usa tipi a dimensione fissa: Come int32_t invece di int
- Test su diverse piattaforme: Windows, Linux, macOS
- Usa CMake o altri sistemi di build: Per gestire le dipendenze
19. Documentazione
Una buona documentazione è essenziale:
- Commenti nel codice: Spiega la logica, non solo cosa fa il codice
- Documentazione delle funzioni: Parametri, valori di ritorno, eccezioni
- Esempi d’uso: Nel codice o in documentazione separata
- README file: Con istruzioni per compilare ed eseguire
- Documentazione API: Se la calcolatrice è una libreria
- Changelog: Per tenere traccia delle modifiche
- Licenza: Specificare i termini di uso
Esempio di documentazione con Doxygen:
/**
* @class ScientificCalculator
* @brief Una calcolatrice scientifica avanzata con funzioni matematiche e memoria
*
* Questa classe implementa una calcolatrice scientifica con:
* - Operazioni aritmetiche di base
* - Funzioni trigonometriche
* - Funzioni logaritmiche ed esponenziali
* - Gestione della memoria
* - Storia dei calcoli
*/
class ScientificCalculator {
// ...
public:
/**
* @brief Calcola la somma di due numeri
* @param a Primo addendo
* @param b Secondo addendo
* @return La somma di a e b
* @throws Nessuna eccezione
*/
double add(double a, double b);
/**
* @brief Calcola il seno di un angolo
* @param x Angolo in radianti
* @param degrees Se true, x è in gradi invece che radianti
* @return Il seno di x
* @throws Nessuna eccezione
*/
double sine(double x, bool degrees = false);
// ...
};
20. Distribuzione
Per distribuire la tua calcolatrice:
- Compilazione: Crea binari per diverse piattaforme
- Packaging: Usa installer o package manager
- Documentazione utente:
- Licenza: Scegli una licenza appropriata (MIT, GPL, etc.)
- Repository: Pubblica su GitHub/GitLab per collaborazione
- CI/CD: Configura integrazione continua per testing automatico
- Rilascio versioni: Usa semantic versioning (v1.0.0)
Esempio di CMakeLists.txt per il packaging:
cmake_minimum_required(VERSION 3.10)
project(ScientificCalculator)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_executable(calculator main.cpp ScientificCalculator.cpp)
target_include_directories(calculator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
# Per Windows
if(WIN32)
set_target_properties(calculator PROPERTIES SUFFIX ".exe")
endif()
# Installazione
install(TARGETS calculator DESTINATION bin)
Conclusione
Creare una calcolatrice in C++ è un progetto eccellente per imparare i fondamenti della programmazione e approfondire concetti avanzati. Da una semplice calcolatrice aritmetica a una completa calcolatrice scientifica con interfaccia grafica, le possibilità sono infinite.
Ricorda che la chiave per un buon programma è:
- Pianificazione accurata delle funzionalità
- Design pulito e modulare del codice
- Gestione robusta degli errori
- Testing completo
- Documentazione chiara
- Ottimizzazione dove necessario
- Manutenibilità per futuro sviluppo
Con le conoscenze acquisite da questa guida, sarai in grado di creare non solo una calcolatrice funzionale, ma anche un’applicazione robusta, efficienti e mantenibile che potrai estendere con nuove funzionalità man mano che le tue abilità di programmazione in C++ crescono.
Buona programmazione!