cmx class + μικρή βιβλιοθήκη

Τύποι μεταβλητών και κλάσεις

Διάφοροι τύποι μεταβλητών που μπορούν άμεσα να χρησιμοποιηθούν στην C++:
char, short, long, int, float, double, bool ,,,
Η γλώσσα παρέχει την δυνατότητα να φτιάξουμε τους δικούς μας τύπους μεταβλητών και να καθορίσουμε την συμπεριφορά τους με τον τρόπο που εμείς θέλουμε. Ο μηχανισμός γιά την δημιουργία δικής μας έμπνευσης τύπων είναι οι κλάσεις. Σε προηγούμενο μάθημα γνωρίσαμε την κλάση cmx με την οποία δημιουργούμε έναν τύπο μιγαδικών μεταβλητών. Τώρα προσθέτουμε στην κλάση λίγες ακόμη συναρτήσεις και φτιάχνουμε μιά μικρή βιβλιοθήκη άλλων συναρτήσεων, που χρησιμοποιούν την κλάση cmx. Τέλος χρησιμοποιούμε αυτήν την κλάση+βιβλιοθήκη σε ένα υποτυπώδες πρόγραμμα.

cmx class

Η κλάση είναι η γνωστή μας cmx με λίγες προσθήκες.
class  cmx
{
public:
	double  x;
	double  y;
public:
	cmx();
	cmx( const cmx & other ); 
	cmx( const double &  xx, const double &  yy );
	virtual ~cmx();                                 // <-- changed
	cmx & operator=( const cmx & other );           // <-- added
	bool  operator==(const cmx & other) const;      // <-- added
	void set( const double &  xx, const double &  yy) ;
	void setPol( const double &  r, const double &  angle) ;
	double re() const;
	double im() const;
	cmx  syz() const;
	double norm() const;
	double argm() const;
	void print( bool line = false) const;

	friend cmx operator+( const cmx  &  a, const cmx  & b );
	friend cmx operator-( const cmx &  a, const cmx & b );
	friend cmx operator*( const cmx &  a, const cmx & b );
	friend cmx operator*( const double &  t, const cmx & b ); // <--added
	friend cmx operator/( const cmx &  a, const cmx & b );    // <--added
};

Συστατικά της κλάσης (προσθήκες)

Παραθέτω μόνο εξηγήσεις για τις γραμμές που προστέθηκαν.
virtual ~cmx(); Η λέξη virtual θα εξηγηθεί αργότερα. Εδώ σημειώνω ότι κλάσεις που ορίζουν υποκλάσεις χρείαζονται virtual destructors. Επειδή στα επόμενα θα ορίσουμε υποκλάσεις της cmx, προετοιμάζουμε το έδαφος από τώρα.
cmx & operator=( const cmx & other ); Ο τελεστής ισότητας. Αν δεν τον ορίσουμε όπως κάνουμε εδώ, ο υπολογιστής χρησιμοποιεί αυτόματα έναν δικό του, όταν χρειασθεί. Στην παρούσα περίπτωση ο αυτόματος κάνει την ίδια δουλειά με τούτον τον συγκεκριμένο.
bool operator==(const cmx & other) const; Τελεστής σύγκρισης δύο μιγαδικών, ωστε να μπορούμε να γράψουμε ...if(z1==z2) κτλ.
friend cmx operator*( const double & t, const cmx & b ); Τελεστής πολλαπλασιασμού μιγαδικού αριθμού με πραγματικό. Δίνει νόημα στην εξίσωση w = t*z όπου z, w μιγαδικοί και t πραγματικός.
friend cmx operator/( const cmx & a, const cmx & b ); Ορίζει την διαίρεση δύο μιγαδικών ώστε να μπορούμε να γράφουμε w=z1/z2 ...

Η υλοποίηση (implementation) των προσθηκών αυτών στην κλάση είναι η εξής:

Υλοποίηση της κλάσης(προσθήκες)

cmx::~cmx(){}

cmx & cmx::operator=( const cmx & other )			// <-- added
{
	if(this == &other ) return *this;
	x=other.x;
	y=other.y;
	return *this;
}

bool	cmx::operator==(const cmx & other) const   // <-- added
{
	if(x==other.x && y==other.y) return true;
	return false;
}

cmx operator*( const double &  t, const cmx & b )  // <--added
{
	return cmx(t*b.x, t*b.y);
}
	

cmx operator/( const cmx & a, const cmx & b  )     // <--added
{
	double q = b.norm();
	if(q!=0.0)			// warning  ???
	{
		return  (1.0/q)*(a*(b.syz()));
	}
	return cmx(0.0, 0.0);
}

this

Σε κάθε συνάρτηση-μέλος μιάς κλάσης είναι διαθέσιμη η μεταβλητή this η οποία περιέχει την διεύθυνση της μεταβλητής από την οποία καλείται η συνάρτηση-μέλος. Ετσι αν γράψω z.enaMelos( ... ) όπου enaMelos(...) είναι κάποια συνάρτηση της cmx, τότε μέσα στο σώμα της enaMelos μπορώ να καλώ την this και να βρίσκω την τρέχουσα τιμή της z που δεν είναι άλλη από την *this. Το * μας πάει πάντοτε από την διεύθυνση στο περιεχόμενο αυτής της διεύθυνσης. Η εντολή
	if(this == &other ) return *this; 
Συγκρίνει τις διευθύνσεις της παρούσης μεταβλητής (αριστερό μέλος της ισότητας) και της μεταβλητής other που παριστάνει την δεξιά πλευρά στην ισότητα z = other; Αν οι διευθύνσεις συμπίπτουν τότε επιστρέφει το περιεχόμενο του this (*this) που δεν είναι άλλο από το z. Μ αυτόν τον τρόπο έχουμε την δυνατότητα να γράψουμε μιά σειρά από ισότητες z1 = z2 = z3 = ... zN = w; οι οποίες εκτελούνται διαδοχικά από τα δεξιά προς τα αριστερά και καθιστούν τα περιεχόμενα όλων των μεταβλητών ίσα με το περιεχόμενο της w. Σημείωσε ότι η & κάνει αντίστροφη δουλειά από την *. Ετσι η &other είναι η διεύθυνση μνήμης στην οποία ευρίσκεται η other.

Αυτή η διάκριση μεταξύ μεταβλητής και διεύθυνσης (στην μνήμη του υπολογιστή) στην οποία ευρίσκεται η μεταβλητή είναι πολύ σημαντική στην C++. Τα πράγματα φαίνονται μπλεγμένα στην αρχή, μέχρι να συνηθήσει κανείς. Ιδιαίτερα όταν λάβει υπόψη του ότι μπορούν να ορισθούν μεταβλητές οι οποίες περιέχουν διευθύνσεις άλλων μεταβλητών. π.χ.

int    n = 32;          // ορισμός ακεραίας μεταβλητής n και εγκαινίαση με τιμή = 32
int*   nAdress = &n;    // ορισμός διεύθυνσης-μεταβλητής (ακεραίου)
Μετά την εκτέλεση της τελευταίας εντολής η nAdress περιέχει την διεύθυνση μνήμης του n. Επομένως το περιεχόμενο *nAdress είναι 32.

Αρχείο συναρτήσεων που χρησιμοποιούν την cmx

Όπως είδαμε προηγουμένως, ο κώδικας που ορίζει μιά κλάση χωρίζεται φυσιολογικά σε δύο μπλοκ. Το πρώτο (interface) ορίζει τις ιδιότητες μιάς κλάσης. Αν ο προγραμματιστής επιλέξει κατάλληλα ονόματα γιά τις συναρτήσεις, στο interface φαίνεται TI ακριβώς κάνει η κλάση. Το ΠΩΣ το κάνει φαίνεται στο άλλο μπλόκ, αυτό της implementation. Την εννοιολογική αυτήν διαίρεση ακολουθούν, κατά κανόνα, οι προγραμματιστές γιά να διαιρέσουν τον κώδικα και να τον τοποθετήσουν σε διαφορετικά αρχεία. Το interface της κλάσης τοποθετήται σε ένα αρχείο με την κατάλληξη .h, ενώ η υλοποίηση (implementation) τοποθετήται σε ένα αρχείο με την κατάλληξη .cpp. Όταν θέλουμε να χρησιμοποιήσουμε κάπου (σε κάποιο άλλο αρχείο με κώδικα) την κλάση cmx, απλά βάζουμε στην αρχή του αρχείου την εντολή
 #include "comx.h" 
που περιέχει τον ορισμό του interface της κλάσης. Η ίδια διαίρεση ακολουθήται και σε αρχεία συναρτήσεων (βιβλιοθήκες) που χρησιμοποιούν μιά κλάση. Ενδεικτικά κατασκευάζουμε μιά μικρή βιβλιοθήκη με 5 συναρτήσεις. Οι ανακοινώσεις των συναρτήσεων τοποθετούνται σε αρχείο με το όνομα cmxFunctions.h. Τα σώματα των συναρτήσεων τοποθετούνται αντίστοιχα σε αρχείο με το όνομα cmxFunctions.cpp.

Interface, δημιουργούμε αρχείο κειμένου με όνομα cmxFunctions.h το οποίο περιέχει:

#ifndef  CMXFUNCTIONS
#define  CMXFUNCTIONS

class  cmx;

double  inner(const cmx & a, const cmx & b );
double  det( const cmx & a, const cmx & b);
cmx     middle(const cmx & a, const cmx & b);

cmx     rotate( const cmx & a );
cmx     power( const cmx & a, int n );

#endif  //CMXFUNCTIONS

Impementation, δημιουργούμε αρχείο κειμένου με όνομα cmxFunctions.cpp το οποίο περιέχει:

#include "comx.h"
#include "cmxFunctions.h"


double  inner(const cmx & a, const cmx & b ) // inner product of two complexes
{
	return a.x*b.x + a.y*b.y;
}

double  det( const cmx & a, const cmx & b)
{
	return a.x*b.y-a.y*b.x;
}

cmx     middle(const cmx & a, const cmx & b)
{
	return cmx( 0.5*(a.x+b.x), 0.5*(a.y+b.y) );
}

cmx     rotate( const cmx & a )
{
	return cmx( -a.y, a.x );
}

cmx     power( const cmx & a, int n )
{
	cmx  z, w;
	int m = n ;
	
	if( (a.x == 0.0)  && (a.y == 0.0) ) {return a;}
	if( n==0 ) { w.set(1.0, 0.0); return w; }
	else if(n<0)
	{
		z = cmx(1.0,0.0)/a;
		m = -n;
	}
	else
	{
		z = a;
	}
	w = z;
	for( int i=0; i<m-1; i++)
	{
		w= w*z;
	}
	return w;
}

Ανάλυση του κώδικα

Αναλύω μόνο τον κώδικα της συνάρτησης power η οποία υψώνει έναν μιγαδικό αριθμό σε μιά ακέραια δύναμη. Σ αυτήν την συνάρτηση παρουσιάζονται κάποιες νέες εντολές της C++.
if( (a.x == 0.0) && (a.y == 0.0) ) {return a;} Το νέο σύμβολο && σημαίνει το λογικό-και. Η εντός του if παρασταση είναι αληθής όταν και το a.x=0 και το a.y=0, δηλαδή όταν ο a είναι ο μηδενικός μιγαδικός.
z = cmx(1.0,0.0)/a; Εδώ βάζω z = 1/a. το cmx(1.0,0.0) είναι το μιγαδικό 1 που ορίζεται μέσω ενός κατασκευαστή της cmx.
for( int i=0; i<m-1; i++) z.print(true); for-βρόγχος. Εδώ είναι ο ελεγκτής του βρόγχου που καθόρίζει ότι α) i είναι μιά ακέραια μεταβλητή που ξεκινά από την τιμή 0, β) η i αυξάνεται μέχρι το m-2 συμπεριλαμβανομένου. γ) η i αυξάνεται με βήμα 1. Μετά κάθε αύξηση εκτελούνται οι εντολές που περιέχονται στο μπλοκ κάτω από τον ελεγκτή, εφόσον το i ικανοποιεί την μεσαία συνθήκη.

Πρόγραμμα που χρησιμοποιεί την βιβλιοθήκη

Σ΄ένα αρχείο με όνομα cmx.cpp γράφω τον κώδικα:
#include "comx.h"				// the interface of cmx class
#include "cmxFunctions.h"      // the interface of the small library
#include <iostream.h>



int main(int argc, char* argv[])
{
	cmx   z(2.3, 7.2), w( 3.5, -6.7 ), u( -10.4, -3.1 ), v;

	cout << "meson toy zw = ";
	middle(z,w).print(true);
	cout << "orizousa tvn z, w =";
	cout << det(z,w) << endl;
	cout << "strofi toy u = ";
	rotate(u).print(true);
	cout << "esoteriko ginomeno ton z, w = ";
	cout << inner(z,w) << endl;

	cout << " z eis tin 3 = " ;
	power(z,3).print(true);
	cout << " z eis tin -4 = " ;
	power(z,-4).print(true);
	cout << " z eis tin  0 = ";
	power(z,0).print(true);

	cout << " 0 eis tin -5 : " ;
	power(v,-5).print(true);
	return 0;
}

Εκτέλεση του προγράμματος

Ασκήσεις

1)   Γράψε ένα πρόγραμμα που χρησιμοποιεί το cmxFunctions και τυπώνει δύο στήλες. Η πρώτη στήλη περιέχει τον ακέραιο n και η δεύτερη τον z εις την n. z ας είναι κάποιος μιγαδικός της επιλογής σας. Το n ας τρέχει από το 0 έως το 15.
2)   Γράψτε ένα πρόγραμμα που ζητά από τον χρήστη α) έναν μιγαδικό (στην μορφή δύο πραγματικών) β) έναν ακέραιο. Κατόπιν τυπώνει τον μιγαδικό υψώνοντάς τον στην δύναμη του ακεραίου και κατόπιν ζητά από τον χρήστη να καθορίσει άν θα συνεχίσει ή θα σταματήσει.
3)   Έχετε όλα τα εργαλεία που χρειάζεστε γιά να κατασκευάσετε μιά μικρή συνάρτηση που επιστρέφει μία (από τις δύο) τετραγωνική ρίζα ενός μιγαδικού.
4)   Γενικεύστε την προηγούμενη κατασκευάζοντας μιά συνάρτηση που επιστρέφει την ν-στή ρίζα ενός μιγαδικού.