Anche per Borland esiste l'ambiente di sviluppo grafico completo, ma qui non
viene trattato. Visitare al riguardo il sito Borland.
Il linguaggio C++
Gli esempi che seguono non prevedono l'interfaccia grafica, e sono compilabili sia con Microsoft Visual C++
che con Borland C++
Gli stessi prodotti sono in grado di compilare sorgenti C
Esempio
#include <iostream.h>
void main()
{
const double cambio=1936.27;
double lire, euro;
cout <<"Quanti euro?\n";
cin>>euro;
lire=euro*cambio;
cout<<"Per "<<euro<<"
euro ci vogliono "<<lire<<" lire\n";
}
Regole relative alle funzioni:
- le istruzioni terminano con ;
- le righe del programma devono essere fra {}
- i parametri di funzione devono essere fra ()
Comunicazione con l'esterno
- includere <iostream.h>
cout <<"Quanti euro?\n";
cin>>euro;
cout <<"Quanti euro?" <<
endl;
cin>>euro;
Tipi di dato
Tipi di dati elementari
char
float
double
int
Tipi meno usati
long
short
signed
unsigned
void
Costanti
const int cambio = 1936.27;
(meglio #define?)
Variabili
Scelta dei nomi
Notazione ungherese in ambiente Windows:
- i puntatori iniziano con p -
- i puntatori far iniziano con lp
- le funzioni iniziano con fn
- gli handle iniziano con h
Io uso:
- pt_ per i puntatori
- fn_ per le funzioni
- st_ per le strutture
Strutture
Dichiarazione
class Listino
{
public:
int codice;
int prezzo;
};
Assegnazione del tipo "Listino" a una variabile:
Listino st_Ricambi;
Accesso alle variabili in una struttura
st_Ricambi.codice = 5;
Strutture nidificate
class Listino
{
public:
int codice;
int prezzo;
};
class Negozio
{
float OraDiApertura, OraDiChiusura;
int NumeroDiSedie;
Listino st_Ricambi;
};
Nota
La parola class si usa anche
per definire le classi, che sono strutture contenenti (anche?) funzioni.
Le strutture si possono definire
anche usando la parola struct come nel linguaggio C (non richiede public).
Espressioni
Operatori di base
* / + - %
Altri operatori
++ -- >> <<
Operatori booleani
> >= < <= == != ! &&
||
Operatori sui bit
- << >> & | ^
Operatori abbreviati
+= -= *= /= %= <<=
>>= &= |= ^=
L'operatore ternario ?:
espressione-condizionale
? espressione1 : espressione2
Priorità degli operatori
() massima precedenza
++ -- ~ !
* / %
+ -
>> <<
< <= > >=
== !=
&
^
|
&&
||
?: minima precedenza
(per sicurezza usare le parentesi)
Flusso del programma
if
if (espressione)
istruzione1;
else
istruzione2;
switch
switch (espressione)
{
case valore1:
istruzione1;
break;
case valore2:
istruzione2;
break;
...
default:
istruzioneDefault;
}
ciclo for
for (espr-iniz; condizione
di permanenza; espr-post)
istruzione;
ciclo while
while (espressione)
istruzione;
ciclo do
do
istruzione;
while (espressione);
Funzioni
void fn_funzione(tipo1 arg1,
tipo2 arg2, ...)
{
......
return espressione;
}
Gestione inline
Se una piccola funzione è richiamata con grande
frequenza (p.e. in un ciclo, conviene usare la 'gestione inline' per memorizzarla.
Quando viene richiamata una funzione,
- vengono salvati i registri della CPU
- viene creato uno stack per gli argomenti e le variabili
locali
- gli argomenti vengono copiati nello stack
- viene caricata la funzione nella cache delle istruzioni
- la funzione viene esguita
- viene ripulito lo stack
- vengono ripristinati i registri della CPU
- viene ricaricata nella cache delle istruzioni la funzione
chiamante
- il controllo viene passato alla funzione chiamante
Con la 'gestione inline', la funzione viene 'esplosa'
nel punto in cui viene richiamata, il che evita tutto il sovraccarico dovuto
ai salvataggi/ripristini, che per funzioni piccole possono richiedere più
tempo della stessa funzione:
inline int Fattoriale(int n)...
Ricorsione
Una funzione che richiama sè stessa, ad esempio
per eseguire la stessa operazione su un sottoinsieme di elementi, si definisce
ricorsiva.
Le funzioni ricorsive non sono molto usate nella realtà.
Parametri variabili
Per indicare che una funzione può rucevere
un numero variabile di parametri (tipo printf) su usano i tre punti (...),
p.e.
int fattoriale(...)
{
}
Puntatori
Mentre una variabile punta sempre alla stessa area di
memoria, l'indirizzo contenuto in un puntatore può essere modificato.
Un puntatore può puntare ad un'area di memoria
dinamica, senza nome.
Un puntatore ad un'area di un certo tipo viene dichiarato
come si dichiara una variabile di quel tipo, col nome preceduto da *;
p.e.:
int *pt_prova;
dichiarare un puntatore ad un intero.
Il carattere * si utilizza anche per 'deindirizzare' il
puntatore, cioè per fare riferimento al valore puntato:
a = *pt_prova;
assegna alla variabile 'a' il valore presente nell'area
a cui punta 'pt_prova'.
*pt_prova = 5;
memorizza il valore 5 nell'area a cui punta 'pt_prova'
(può essere, p.e., una variabile oppure l'elemento di un array)
Per assegnare 'direttamente' ad un puntatore l'indirizzo
di una variabile, si usa '&':
pt_prova = &var;
carica nel puntatore 'pt_prova' l'indirizzo della variabile
'var'.
Per caricare in un puntatore l'indirizzo di un'area dinamica
appena creata, si usa new:
int *pt_intero;
pt_intero = new int;
idem per una nuova struttura:
Listino *pt_st_Ricambi;
pt_st_Ricambi = new Listino;
Per liberare la memoria occupata da un oggetto allocato
dinamicamente, si fa il delete del puntatore a quell'oggetto:
delete pt_intero;
pt_intero = 0; // per sicurezza
Esempio
#include <iostream.h>
void main()
{
int numero, *pt_num;
pt_num = №
cout << "Immetti un numero:
";
cin >> numero;
cout << "Hai immesso "
<< *pt_num << " cioè " << numero << endl;
cout << "Il valore è
memorizzato all'indirizzo hex " << pt_num;
}
Strutture
Se pt_struttura punta a una struttura, per indirizzarne
un elemento si può utilizzare la forma
(*pt_struttura).elemento
(come se fosse st_nome_struttura.elemento)
oppure la notazione semplificata
pt_struttura->elemento
Stringhe
Una stringa è una serie di caratteri il cui
ultimo carattere è '\0'.
Il nome di una stringa è un puntatore al primo
carattere della stringa.
La maggior parte delle funzioni di libreria che agiscono
sulle stringhe inizia con i caratteri 'str' (strcpy, strlen, strcat, ecc.).
Il linguaggio C++ include un oggetto chiamato 'classe
per stringhe ANSI' utile per creare e manipolare le stringhe.
Argomeni indirizzo
Invece di far lavorare le funzioni su variabili globali,
conviene passare degli argomenti, che la funzione non potrà modificare.
E' possibile passare ad una funzione non diretttamente
un argomento, ma un puntatore ad un argomento. In questo caso la funzione
chiamata può modificare direttamente la variabile nella funzione
chiamante:
Esempio - versione C
/* sfera.c */
#include <stdio.h>
#define PI_GRECO 3.14159
int main()
{
int calcola(float raggio, float
*pt_area, float *pt_volume);
float raggio;
float area;
float volume;
printf("Digita il valore del
raggio: ");
scanf("%f",&raggio);
calcola(raggio, &area, &volume);
printf("Area della sfera di
raggio %f: %f\n",raggio, area);
printf("Volume della sfera di
raggio %f: %f\n",raggio, volume);
return(0);
}
int calcola(float raggio, float
*pt_area, float *pt_volume)
{
*pt_area=raggio*raggio*4.0*PI_GRECO;
*pt_volume=raggio*raggio*raggio*4.0/3.0*PI_GRECO;
return(0);
}
Esempio - versione C++
/* sfera.cpp */
#include <iostream.h>
int main()
{
int calcola(float raggio, float
&area, float &volume);
float raggio;
float area;
float volume;
int retcode;
cout << "Digita il valore
del raggio: ";
cin >> raggio;
retcode = calcola(raggio, area,
volume);
cout << "Area della sfera
di raggio " << raggio << "=" << area << endl;
cout << "Volume della
sfera di raggio " << raggio << "=" << volume <<
endl;
return(0);
}
int calcola(float raggio, float
&area, float &volume)
{
const float PI_GRECO = 3.14159;
area=raggio*raggio*4.0*PI_GRECO;
volume=raggio*raggio*raggio*4.0/3.0*PI_GRECO;
return(0);
}
Per evitare che un argomento, p.e. una struttura, passato
per indirizzo, venga modificato, si deve specificare const:
int prova(const Listino &st_Ricambi)
{
...
}
Array e tipi enumerativi
Un 'tipo enumerativo' (enum) è un elenco
di costanti, p.e.:
enum {scelta0, scelta1, scelta2};
equivale a scrivere
const int scelta0 = 0;
const int scelta1 = 1;
const int scelta2 = 2;
in quanto enum assegna automaticamente i valori 0, 1,
2
Un array è una sequenza di elementi di uno
stesso tipo, indicizzati a partire da 0.
Il numero di elementi viene indicato nella definizione,
fra []:
int prova[20]; //indice da 0
a 19
Gli elementi dell'array possono essere indirizzati mettendo
l'indice fra [], oppure, siccome il nome dell'array è un puntatore
al primo elemento, sommando l'indice al nome, p.e.:
a = prova[3];
equivale a
a = *(prova+3);
L'inizializzazione di un array può avvenire alla
definizione:
int giorni_mese[12] = {31,28,31,30,31,30,31,31,30,31,30,31);
oppure uno per uno, oppure, se possibile, in un ciclo.
Una stringa è costituita da un'array di
caratteri, quindi il suo nome è un puntatore al primo carattere;
la stringa può dunque essere definita in due modi:
char nome[20];
char cognome[] = "Rossi";
oppure
char *nome;
char *cognome = "Rossi";
AppWizard: generazione di applicazioni
Uso di AppWizard
Provare ad eseguire le seguenti operazioni:
--- Menu/File - New
- MFC AppWizard (exe)
- Project name: un nome, p.e. 'prova' (viene creata
la subdir 'prova' nella dir 'MyProjects')
- Step 1:
Multiple
Documents
- Step 2:
No
DB
- Step 3:
No
Compound, other:ActiveX
- Step 4:
Features: Docking ToolBar
-
Initial Status Bar
-
Printing with preview
-
3D controls
-
n.4 recent files
-
(eventuale caption su Advanced)
- Step 5:
Comments,
Shared
DLLs
- Step 6:
vengono visualizzate le classi create, p.e.:
Classi generate
da AppWizard
| Class name |
Header file |
Base Class |
Implementation file |
| CprovaApp |
prova.h |
CWinApp |
prova.cpp |
| CMainFrame |
MainFrm.h |
CMDIFrmWnd |
MainFrm.cpp |
| CChildFrame |
ChildFrm.h |
CMDIChildWnd |
ChildFrm.cpp |
| CprovaDoc |
provaDoc.h |
CDOcument |
provaDoc.cpp |
| CprovaView |
provaView.h |
CView |
provaView.cpp |
(Le voci in corsivo non si possono modificare)
(Se si vuole che il programma sia in grado di modificare
files di testo, selezionare la classe CprovaView e modificare la BaseClass
da 'CView' a 'CEditView')
Premendo finish
viene visualizzata una finestra informativa del tipo:
Application type
of prova:
Multiple Document Interface
Application targeting:
Win32
Classes to be created:
Application: CProvaApp
in prova.h and prova.cpp
Frame: CMainFrame in
MainFrm.h and MainFrm.cpp
MDIChildFrame: CChildFrame
in ChildFrm.h and ChildFrm.cpp
Document: CProvaDoc
in provaDoc.h and provaDoc.cpp
EditView: CProvaView
in provaView.h and provaView.cpp
Features:
+ Initial toolbar in
main frame
+ Initial status bar
in main frame
+ Printing and Print
Preview support in view
+ 3D Controls
+ Uses shared DLL implementation
(MFC42.DLL)
+ ActiveX Controls support
enabled
+ Localizable text in:
Italiano [standard] |
Dopo l'OK AppWizard crea un file di risorse, una serie
di file C++ e di header e alcune immagini bitmap;
Alla fine viene visualizzata la finestra di WorkSpace,
che permette di visualizzare:
WorkSpace
| Classi |
Risorse |
Files |
Guida VisualC++ |
| CAboutDlg |
Accelerator |
Source files |
|
| CChidFrame |
Dialog |
Header files |
|
| CMainFrame |
Icon |
Resource files |
|
| CprovaApp |
Menu |
readme.txt |
|
| CprovaDoc |
String Table |
(globals) |
|
| Cprovaview |
ToolBar |
|
|
|
|
|
|
(La finestra di WorkSpace si può disporre in verticale
o in orizzontale)
Compilazione
Menu Build - Build prova.exe per compilare
La compilazione genera files OBJ, che vengono uniti a
codice prelevato dalle librerie;
vengono quindi aggiunte le risorse (finestre di dialogo,
menu, icone, ecc.) e viene creato il file eseguibile '.exe', che porà
essere lanciato dall'esterno o dall'interno (Run)
Viene creato anche un file README.TXT che descrive l'applicazione
appena generata.
L'applicazione 'prova' è in grado di creare, aprire,
modificare, stampare uno o più files di testo; si possono aprire
più finestre contemporaneamente. (p.e. si possono aprire due documenti,
e fare Cut&Paste da uno all'altro)
ClassWizard e WizardBar: modifica
di applicazioni
WizardBar contiene tre 'combo':
-
la prima combo permette di selezionare una classe
-
la seconda combo permette di selezionare i filtri su quella
classe
-
la terza combo permette di evidenziare la parte di classe
selezionata tramite i filtri
La freccia in basso permette di scegliere l'azione da eseguire
sul membro o sulla classe, e la bacchetta magica da' il via all'esecuzione.
Ciascuna delle sei classi MFC elencate nella prima
combo ha uno scopo ben definito, relativo ad una determinata parte dell'applicazione:
-
CprovaApp controlla il comportamento di base dell'applicazione
-
CMainFrame controlla la finestra principale
-
CChildFrame controlla le finestre 'figlie'
-
CAboutDlg controlla la finestra di dialogo 'Informazioni'
richiesta con '?'
-
CprovaDoc controlla il modo in cui i documenti memorizzano
i dati
-
CprovaView controlla la visualizzazione dei documenti
Per modificare la finestra di dialogo
informazioni:
- Selezionare 'IDD_ABOUTBOX' fra le risorse 'Dialog' (Dialogo
(IDD) relativo al menu ?(ABOUT))
- Utilizzare i controlli per modificare la finestra di
dialogo
- Save
- Rebuild All
Per aggiungere una nuova finestra
di dialogo:
- Selezionare 'Insert Dialog' col tasto destro sulla cartella
delle risorse Dialog
Viene proposta una finestra di dialogo di default, con
nome IDD_DIALOG1, che si può personalizzare con i controlli,
che possono contenere:
- immagini
- testo statico
- testo editabile
- raggruppamenti
- pulsanti
- Check box
- radio box
- combo box
- list box
- scroll bar orizzontali
- scroll bar verticali
- spin
- progress
- slider
- hot key
- list control
- tree control
- tab control
- animazioni
- rich edit
- custom control
Per associare una classe alla
nuova finestra di dialogo:
- Doppio click sulla finestra di dialogo
- Create new class - OK
- Nome della classe (p.e.): CNuovaClasse: propone
nome file=NuovaClasse.cpp,
eredita la 'Base Class' CDialog (perchè ClassWizard
è stato chiamato da una finestra di dialogo).
Alla nuova classe viene associata la finestra di dialogo
'chiamante', p.e. IDD_DIALOG1.
IDD è il prefisso che si usa di solito per
le finestre di dialogo.
- OK, OK
Per inserire in un menu la chiamata
alla nuova classe:
- Doppio click sulla risorsa IDR_(nome_pgm)TYPE (p.e.IDR_PROVATYPE)
nella cartella dei menu (Esiste anche un altro menu, IDR_MAINFRAME, che
è relativo alla finestra principale e viene visualizzato quando
non c'è nessun documento aperto.
- Scegliere una casella vuota in fondo a qualcuno dei
menu, oppure usare INS per inserire una nuova voce
- Click destro sulla casella vuota, inserire Caption
e Prompt
- Chiudere e riaprire per vedere l'ID della nuova voce
di menu, p.e. ID_UITEM32771
- Se si ricompila il programma ora, sarà presente
la nuova voce di menu, ma non sarà attiva
Per agganciare al programma la
nuova finestra di dialogo:
-
- In WizardBar verificare che sia presente la nuova classe
-
- selezionare la classe C(nome_pgm)View, p.e. CPROVAView
-
- Fra gli elementi della classe, scegliere la voce di menu
appena creata (p.e.ID_MENUITEM32771)
-
- Sull'elemento COMMAND premere la bacchetta magica per aggiungere
la funzione associata alla opzione del menu (new windows message/event)
-
- ADD and EDIT
-
- Viene proposto come nome della nuova funzione ON(nuova
opzione menu senza ID), p.e.
ONMENUITEM32771
-
- Dopo la riga di commento che viene proposta inserire la
riga di codice:
(nome_classe).DoModal();
p.e. CNuovaClasse().DoModal();
per chiamare
la funzione DoModal() (ereditata da CDialog) all'interno della classe appena
creata per gestire la finestra di dialogo
-
- Aggiungere la .h di definizione della classe in testa al
codice editato:
#include "(nome_classe senza C).h"
#include "NuovaClasse.h"