cmx class

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

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

cmx class

class  cmx
{
public:
	double  x;
	double  y;
public:
	cmx();
	cmx( const cmx & other ); // very important copy-constructor
	cmx( const double &  xx, const double &  yy );
	~cmx();
	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 );
};

Συστατικά της κλάσης

public:
  double  x;
  double  y;
Οι δύο μεταβλητές της κλάσης: δύο πραγματικοί αριθμοί διπλής ακρίβειας (double) προορισμένοι να κρατούν το πραγματικό και φανταστικό μέρος ενός μιγαδικού αριθμού.
cmx(); Ο βασικός κατασκευαστής (default constructor) της κλάσης.
 cmx( const cmx & other );
Ένας άλλος πολύ σημαντικός κατασκευαστής, κατασκευαστής-αντιγραφής, (copy-constructor) δημιουργεί αντίγραφο ενός μιγαδικού. Σημείωσε την const που καθορίζει ότι η τιμή της μεταβλητής που ακολουθεί μπορεί να χρησιμοποιηθεί από την συνάρτηση γιά τους σκοπούς της αλλά δεν επιτρέπεται να αλλοιωθεί η τρέχουσα τιμή της.
Σημείωσε ακόμη την & που συμβολίζει την διεύθυνση όπου είναι αποθηκευμένη η μεταβλητή που ακολουθεί (και όχι την πραγματική τιμή της).
 cmx( const double &  xx, 
      const double &  yy );
Ενας τρίτος κατασκευαστής που φτιάχνει τον μιγαδικό από το πραγματικό και φανταστικό μέρος του. Σημείωσε πάλι τα const και & .
Υπό κανονικές συνθήκες μιά συνάρτηση f(x,y, ...) κάνει αντίγραφα των x, y, ... μέσα στην μνήμη και δουλεύει με τα αντίγραφα. Το & υποχρεώνει την f να δουλέψει με τα ίδια τα αντικείμενα που συμβολίζουν τα x, y, ... και έτσι να γλυτώσει τον χρόνο της αντιγραφής.
~cmx(); Ο γνωστός μας καταστροφέας, και σ΄αυτήν την κλάση δεν κάνει τίποτε.
void set(const double & xx, 
         const double &  yy);
Μιά συνάρτηση που θέτει τις τιμές πραγματικού/φανταστικού μέρους του μιγαδικού.
 void setPol(const double & r,
       const double &  angle);
Μιά συνάρτηση που ορίζει τον μιγαδικό από τις πολικές συντεταγμένες του.
 double re() const;
 double im() const;
Δύο απλές συναρτήσεις που επιστρέφουν αντίστοιχα πραγματικό/φανταστικό μέρος του μιγαδικού. Σημείωσε πάλι το const , αυτή την φορά στο τέλος της συνάρτησης. Αυτό απαγορεύει στην συνάρτηση-μέλος της κλάσης να αλλάξη την τιμή των μεταβλητών της.
 cmx  syz() const;
 double norm() const;
 double argm() const;
Τρεις απλές συναρτήσεις που επιστρέφουν αντίστοιχα τον συζυγή, το μέτρο και το όρισμα ενός μιγαδικού.
void print( bool line
 = false) const;
Μιά συνάρτηση που τυπώνει τον μιγαδικό και αλλάζει γραμμή όταν η λογική μεταβλητή έχει τιμή true. Το =false σημαίνει ότι αν καλέσουμε την συνάρτηση γράφοντας print() τούτο ισοδυναμεί με print(false).

  
  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 που είναι μπροστά τους. Οι φίλες συναρτήσεις χαρακτηρίζονται από το ότι έχουν πρόσβαση στις μεταβλητές της κλάσης. Εδώ τις χρησιμοποιούμε για να ορίσουμε τις πράξεις + , - , * γιά μιγαδικούς. Οι φίλες συναρτήσεις δεν είναι συναρτήσεις μέλη της κλάσης. Δεν μπορούμε να αναφερθούμε σ΄αυτές με την γνωστή τελεία, γράφοντας z.f(...) όπως κάνουμε με τις συναρτήσεις μέλη.
Το παραπάνω μπλοκ αναγνωρίζεται από την C++ σαν ορισμός (interface) μιάς νέας κλάσης με όνομα cmx. Αντίθετα με την Hello κλάση δεν έγραψα γιά καμμία συνάρτηση το σώμα της μέσα στον ορισμό. Ακόμη και γιά τον καταστροφέα που δεν κάνει τίποτε. Αυτή είναι μιά καλή τακτική που επιτρέπει να αλλάζουμε αργότερα τα σώματα των συναρτήσεων, χωρίς να πηράζουμε το αρχείο που περιέχει τον ορισμό τους.
Η υλοποίηση (implementation) της κλάσης είναι η εξής:

Υλοποίηση της κλάσης(implementation)

// constructors (may have many)
cmx::cmx(){x=y=0.0;}
cmx::cmx( const cmx & other ):x(other.x), y(other.y){}
cmx::cmx( const double & xx, const double &  yy ):x(xx),y(yy) {}

// destructor (only one)
cmx::~cmx(){}

// set-get functions
void cmx::set( const double &  xx, const double &  yy) { x = xx; y=yy; }
void cmx::setPol( const double &  r, const double &  angle) 
{ 
	x = r*cos(angle); 
	y = r*sin(angle);
}
double cmx::re() const {return x; }
double cmx::im() const {return y; }

// some functions
cmx  cmx::syz()	 const 	// conjugate complex
{ 
	cmx z; 
	z.x=x; 
	z.y=-y; 
	return z;
}

double cmx::norm() const  // norm of a complex
{
	double q ( x*x + y*y ); 
	return sqrt(q); 
}

double cmx::argm() const  // argument of a complex
{
	return atan2(y,x); 
}

void cmx::print( bool line) const //  print a complex
{ 
	cout << " ( " << x <<" , " << y << " ) " ; 
	if(line) cout << endl;  // change line
}


cmx operator+( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x + b.x;
	z.y = a.y + b.y;
	return z;
}

cmx operator-( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x - b.x;
	z.y = a.y - b.y;
	return z;
}

cmx operator*( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x *b.x - a.y*b.y;
	z.y = a.x * b.y + a.y*b.x;
	return z;
}

Βλέπουμε ότι στην υλοποίηση ορίζεται το σώμα των συναρτήσεων (member functions) της κλάσης καθώς και οι φίλες συναρτήσεις της κλάσης. Η C++ αναγνωρίζει ότι πρόκειται για μέλη της κλάσης cmx από την προσθήκη του cmx:: μπροστά από κάθε συνάρτηση. Όπως ανέφερα παραπάνω οι φίλες συναρτήσεις, που ορίζουν τις βασικές πράξεις με μιγαδικούς, δεν είναι συναρτήσεις μέλη της κλάσης, γι΄αυτό δεν έχουν το cmx:: μπροστά τους.

Οργάνωση του κώδικα μιας κλάσης

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

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

#ifndef  CMXCLASS           /* class cmx interface */
#define  CMXCLASS

class  cmx
{
public:
	double  x;
	double  y;
public:
	cmx();
	cmx( const cmx & other ); // very important copy-constructor
	cmx( const double &  xx, const double &  yy );
	~cmx();
	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 );
};

#endif // CMXCLASS

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

#include <iostream.h>    // class cmx implementing complex numbers
#include <math.h>        // to achieve greater precision use double 
#include "comx.h"         // the interface of cmx class

// constructors (may have many)
cmx::cmx(){x=y=0.0;}
cmx::cmx( const cmx & other ):x(other.x), y(other.y){}
cmx::cmx( const double & xx, const double &  yy ):x(xx),y(yy) {}

// destructor (only one doing nothing)
cmx::~cmx(){}

// set-get functions
void cmx::set( const double &  xx, const double &  yy) { x = xx; y=yy; }
void cmx::setPol( const double &  r, const double &  angle) 
{ 
	x = r*cos(angle); 
	y = r*sin(angle);
}
double cmx::re() const {return x; }
double cmx::im() const {return y; }

// some functions
cmx  cmx::syz()	 const 	// conjugate complex
{ 
	cmx z; 
	z.x=x; 
	z.y=-y; 
	return z;
}

double cmx::norm() const  // norm of a complex
{
	double q ( x*x + y*y ); 
	return sqrt(q); 
}

double cmx::argm() const  // argument of a complex
{
	return atan2(y,x); 
}

void cmx::print( bool line) const //  print a complex
{ 
	cout << " ( " << x <<" , " << y << " ) " ; 
	if(line) cout << endl;  // change line
}


cmx operator+( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x + b.x;
	z.y = a.y + b.y;
	return z;
}

cmx operator-( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x - b.x;
	z.y = a.y - b.y;
	return z;
}

cmx operator*( const cmx & a, const cmx & b  ) 
{
	cmx z;
	z.x = a.x *b.x - a.y*b.y;
	z.y = a.x * b.y + a.y*b.x;
	return z;
}

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

Σ΄ένα αρχείο με όνομα cmx.cpp γράφω τον κώδικα:
#include <iostream.h>
#include "comx.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 << "z =";
	z.print(true);
	cout << "w =";
	w.print(true);
	cout << "u =";
	u.print(true);
	cout << "v =";
	v.print(true);

	cout << " conj of z " ;
	z.syz().print(true);
	v = z+w;
	cout << " sum z+w = " ;
	v.print(true);
	v = z*w;
	cout << " product z*w = ";
	v.print(true);
	v.setPol( 3., 1.2 );
	cout << " v in cartesian : " ;
	v.print(true);
	cout << " norm and argument = " << endl;
	cout << v.norm() << endl;
	cout << v.argm() << endl;
	return 0;
}

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

#include <iostream.h> #include "comx.h" Η πρώτη γνωστή και από προηγούμενα προγράμματα συνδέει με την βασική βιβλιοθήκη εισόδου-εξόδου δεδομένων. Η δεύτερη εντολή συνδέει με την κλάση cmx.
cmx  z(2.3, 7.2), 
     w( 3.5, -6.7 ), 
     u( -10.4, -3.1 ), 
     v;
Ορισμός 4 μεταβλητών τύπου cmx, στις τρείς από τις οποίες δίδονται και αρχικές τιμές. Η C++ χρησιμοποιεί γιά τις 3 πρώτες τον 3ο κατασκευαστή. Γιά την τελευταία v χρησιμοποιεί τον πρώτο κατασκευαστή (βασικό).
cout << "z =";
z.print(true);
Το ζεύγος εντολών εδώ τυπώνει z = ( 2.3 , 7.2 ) και αλλάζει γραμμή (λόγω του true στην z.print(true)). Ανάλογα και τα υπόλοιπα τρία ζεύγη εντολών τυπώνουν τους μιγαδικούς που ορίστηκαν στην αρχική γραμμή.
cout << " conj of z " ;
z.syz().print(true);
Ενδιαφέρον εδώ έχει η σύνθετη εντολή z.syz().print(true); που τυπώνει τον συζυγή του z. Σημείωσε εδώ ότι πρώτα εκτελείται η z.syz() της οποίας το αποτέλεσμα είναι πάλι τύπου cmx και επομένως έχει νόημα η z.syz().print(true);.
v = z+w;
Σ αυτήν την μικρή εντολή κρύβεται λίγη από την γοητεία της C++. Προσθέτουμε μιγαδικούς με το σύμβολο που έχουμε συνηθίσει! Το νόημα του συμβόλου καθορίζει η φίλτατη συνάρτηση operator+(...). Σημείωσε ότι η εντολή αυτή είναι ισοδύναμη με την v = operator+(z,w); .
cout << " sum z+w = " ;
v.print(true);
Ζεύγος εντολών που τυπώνουν sum z+w = ( 5.8 , 0.5 ) και αλλάζουν γραμμή.
v = z*w;
cout << " product z*w = ";
v.print(true);
Και εδώ η πρώτη με το * βρίσκει το γινόμενο και οι δύο άλλες τυπώνουν το αποτέλεσμα. Η πρώτη εντολή είναι πάλι ισοδύναμη με την v = operator*(z,w); .
  v.setPol( 3., 1.2 );
Υπολογίζει τον μιγαδικό από τις πολικές συντεταγμένες του (z = 3(cos(1.2)+isin(1.2)). Οι υπόλοιπες εντολές τυπώνουν τις συντετγαμένες του z και ξαναβρίσκουν απ΄ αυτές το μέτρο και το όρισμα του z, που φυσικά συμπίπτουν με τα αρχικά 3 και 1.2.

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

Ασκήσεις

1)   Όρισε μιά φίλη συνάρτηση operator/(...,...) η οποία κάνει την δίαίρεση δύο μιγαδικών.
2)   Όρισε μιά συνάρτηση μέλος της κλάσης που στρίβει τον μιγαδικό κατα 90 μοίρες περί το (0,0).
3)   Όρισε μιά φίλη συνάρτηση της κλάσης που επιστρέφει την ορίζουσα δύο μιγαδικών αριθμών (ορίζουσα του πίνακα με γραμμές τις συντεταγμένες των δύο μιγαδικών). Παρόμοια μιά φίλη που επιστρέφει το εσωτερικό γινόμενο δύο μιγαδικών.
4)   Όρισε μιά φίλη συνάρτηση της κλάσης operator*(...,...) που επιστρέφει το γινόμενο tz ενός πραγματικού αριθμού t με έναν μιγαδικό z.
5)   Όρισε μιά συνάρτηση της μορφής cmx middle(const cmx & a, const cmx & b) που υπολογίζει το μέσον του τμήματος ab.