Pages

Monday, November 29, 2010

C++ 102: Constructors, ο δείκτης this και η κλάση String

Στο προηγούμενο επεισόδιο είδαμε μια εισαγωγή στις κλάσεις, συναρτήσεις πρόσβασης στις ιδιότητες της κλάσης (setters & getters) και μερικές βασικές διαφορές ανάμεσα στη C και τη C++.

Τώρα θα δούμε συναρτήσεις εγκατάστασης (constructors), τον δείκτη this, και θα κάνουμε και μια εισαγωγή στην κλάση String.



Constructors:

Είδαμε την προηγούμενη φορά έναν τρόπο να δίνουμε τιμές στις ιδιότητες (μεταβλητές) των αντικειμένων μας, χρησιμοποιώντας συναρτήσεις όπως οι setName() και setAge().

Πολλές φορές όμως είναι χρήσιμο και βολικό να αρχικοποιούμε τα αντικείμενα μας κατά τη δημιουργία τους.

Οι constructors είναι συναρτήσεις οι οποίες καλούνται και εκτελούνται αυτόματα κατά τη δημιουργία του αντικειμένου και έχουν ακριβώς το ίδιο όνομα με την κλάση:

Αν η κλάση μας λέγεται Person τότε ο constructor θα πρέπει να ονομαστεί Person. Τα person pErson PERSON PeRSoN κλπ δεν θα θεωρηθούν constructors και θα προκαλέσουν σφάλμα κατά το compile.

Ακριβώς επειδή καλούνται αυτόματα, μπορούν να αρχικοποιήσουν τα αντικείμενα μας όπως θέλουμε και με ό,τι τιμές θέλουμε. Γι' αυτόν τον λόγο επίσης, μιας που δεν επιστρέφουν τίποτα δεν χρειάζεται να δηλώσουμε τύπο, ούτε καν void.

Παρακάτω ακολουθεί ένα παράδειγμα χρήσης των constructors:

#include <iostream>
#include <cstring>

using namespace std;

class Person
{
    private:
        char name[20];
        int age;


    public:
        Person()
        {
            strcpy(name, "Mitsaras");
            age = 25;
        }
        void setName(char* newName)
        {
            strcpy(name, newName);
        }
        void setAge(int newAge)
        {
            age = newAge;
        }


        char* getName()
        {
            return name;
        }
        int getAge()
        {
            return age;
        }
};

int main()
{
    Person a,b;

    cout<<"Your name is: "<<a.getName();
    cout<<"\nAnd your age is: "<<a.getAge()<<endl;
    cout<<"Your name is: "<<b.getName();
    cout<<"\nAnd your age is: "<<b.getAge()<<endl;


    return 0;
}



Και τα 2 αντικείμενα σε αυτή την περίπτωση έχουν -όπως φαίνεται από τον constructor- τις ίδιες τιμές.
Συνήθως σε μια περίπτωση όπως η παραπάνω δίνονται (προγραμματιστικά) ουδέτερες τιμές πχ για όνομα ένα κενό string και για ηλικία 0.

Επίσης συνηθίζεται να χρησιμοποιούνται περισσότεροι του ενός constructors:
Ένας ο οποίος θα δίνει ουδέτερες τιμές και ένας που θα δίνει τις τιμές που θέλουμε κατά τη δημιουργία των αντικειμένων:

#include <iostream>
#include <cstring>

using namespace std;

class Person
{
    private:
        char name[20];
        int age;


    public:
        Person()
        {
            strcpy(name, "");
            age = 0;
        }
        Person(char* newName, int newAge)
        {
            strcpy(name, newName);
            age = newAge;
        }

        
        void setName(char* newName)
        {
            strcpy(name, newName);
        }
        void setAge(int newAge)
        {
            age = newAge;
        }


        char* getName()
        {
            return name;
        }
        int getAge()
        {
            return age;
        }
};

int main()
{
    Person a,b("mitsos", 15);

    cout<<"Object a:\n";
    cout<<"Your name is: "<<a.getName();
    cout<<"\nAnd your age is: "<<a.getAge()<<endl<<endl;
    cout<<"Object b:\n";
    cout<<"Your name is: "<<b.getName();
    cout<<"\nAnd your age is: "<<b.getAge()<<endl;


    return 0;
}


Όπως βλέπουμε έχουμε 2 constructors: Έναν ο οποίος -όπως και πριν- δε δέχεται ορίσματα και έναν ο οποίος δέχεται ορίσματα και δίνει αυτές τις τιμές στο αντικείμενο, λειτουργώντας ουσιαστικά ως συνάρτηση setter.
Ο διαχωρισμός για το ποιος από τους 2 constructors θα χρησιμοποιηθεί γίνεται αυτόματα βάσει του αν έχουν δοθεί ορίσματα.


Ο δείκτης this:

Αρχικά μη σας τρομάζει η λέξη "δείκτης".
Το this χρησιμοποιείται μέσα στις συναρτήσεις της κλάσης και αναφέρεται στις μεταβλητές του εκάστοτε αντικειμένου. Παρακάτω ακολουθεί ο προηγούμενος κώδικας με τη χρήση του δείκτη this:

#include <iostream>
#include <cstring>

using namespace std;

class Person
{
    private:
        char name[20];
        int age;


    public:
        Person()
        {
            strcpy(this->name, "");
            this->age = 0;
        }
        Person(char* newName, int newAge)
        {
            strcpy(this->name, newName);
            this->age = newAge;
        }

        
        void setName(char* newName)
        {
            strcpy(this->name, newName);
        }
        void setAge(int newAge)
        {
            this->age = newAge;
        }


        char* getName()
        {
            return this->name;
        }
        int getAge()
        {
            return this->age;
        }
};

int main()
{
    Person a,b("mitsos", 15);

    cout<<"Object a:\n";
    cout<<"Your name is: "<<a.getName();
    cout<<"\nAnd your age is: "<<a.getAge()<<endl<<endl;
    cout<<"Object b:\n";
    cout<<"Your name is: "<<b.getName();
    cout<<"\nAnd your age is: "<<b.getAge()<<endl;


    return 0;
}



Εύλογα μπορεί να αναρωτηθεί κανείς "και σε τόσα παραδείγματα που δεν το χρησιμοποίησες, πώς έτρεχαν;" Well, δεν είναι 100% απαραίτητο πάντα, αλλά κάνει τον κώδικα πιο κατανοητό όχι μόνο για τον προγραμματιστή αλλά και για τον compiler. Παρακάτω έχω κάνει κάποιες αλλαγές στον 2ο constructor και στους 2 setters για να γίνει πιο κατανοητό το τι εννοώ:


#include <iostream>
#include <cstring>

using namespace std;

class Person
{
    private:
        char name[20];
        int age;


    public:
        Person()
        {
            strcpy(this->name, "");
            this->age = 0;
        }
        Person(char* name, int age)
        {
            strcpy(this->name, name);
            this->age = age;
        }

        
        void setName(char* name)
        {
            strcpy(this->name, name);
        }
        void setAge(int age)
        {
            this->age = age;
        }


        char* getName()
        {
            return this->name;
        }
        int getAge()
        {
            return this->age;
        }
};

int main()
{
    Person a,b("mitsos", 15);

    cout<<"Object a:\n";
    cout<<"Your name is: "<<a.getName();
    cout<<"\nAnd your age is: "<<a.getAge()<<endl<<endl;
    cout<<"Object b:\n";
    cout<<"Your name is: "<<b.getName();
    cout<<"\nAnd your age is: "<<b.getAge()<<endl;


    return 0;
}



Βλέπουμε ότι χρησιμοποιώντας τον δείκτη this ο compiler δεν μπερδεύεται όταν κάνουμε την ανάθεση this->age = age. Αντίθετα αν γράφαμε age = age δεν θα έκανε την ανάθεση στην ιδιότητα age του αντικειμένου.


Η κλάση string

Σε προηγούμενο παράδειγμα είδαμε ότι για να κάνουμε ανάθεση της μεταβλητής ενός string σε ένα άλλο έπρεπε να χρησιμοποιήσουμε την συνάρτηση strcpy(). Θα δούμε τώρα μια κλάση της C++ που θα μπορούμε να κάνουμε τις αναθέσεις με απλό "=" όπως δηλαδή κάνουμε με όλες τις μεταβλητές μας. Επίσης η κλάση αυτή μας δίνει πολλές ευκολίες για να παίξουμε με τα strings.
Στο παράδειγμα που ακολουθεί θα δείξω τις σημαντικότερες συναρτήσεις/ευκολίες που μας δίνει η χρήση αυτής της κλάσης.


/* 

#include <iostream>
#include <string>

using namespace std;

int main()
{

    //1o meros: Anathesi timwn
    string myString1, myString2="thrasyvoulas", myString3;
    int length, position;

    myString3 = "mitsos";

    cout<<"Part 1:"<<endl;
    cout<<"String 1:"<<myString1<<endl;
    cout<<"String 2:"<<myString2<<endl;
    cout<<"String 3:"<<myString3<<endl<<endl;

    //2o meros: anathesi tis timis enos string se ena allo
    //xwris tin xrisi tis strcpy:
    myString1 = myString2;

    cout<<"Part 2:"<<endl;
    cout<<"String 1:"<<myString1<<endl;
    cout<<"String 2:"<<myString2<<endl;
    cout<<"String 3:"<<myString3<<endl<<endl;

    //3o meros: mikos string
    length = myString1.length();
    cout<<"Part 3:"<<endl;
    cout<<"Length of 1st string:"<<length<<endl;
    cout<<"Length of 2nd string:"<<myString2.length()<<endl;
    cout<<"Length of 3rd string:"<<myString3.length()<<endl<<endl;

    //4o meros: replace
    //apo tin thesi 5 ws tin thesi 9 tou myString1
    //vale ta periexomena tou myString3
    cout<<"Part 4:"<<endl;
    cout<<myString1.replace(5,9, myString3)<<endl<<endl;

    //5o meros find
    //i find dexetai ena string to opoio to psaxnei sto string apo
    //to opoio klithike (myString1 stin periptwsi mas) kai epistrefei
    //tin thesi stin opoia vrethike H -1 an _DEN_ vrethike.

    myString1 = "Tria poulakia kathotan k apo psila agnanteuan";
    string searchFor = "otan";
    position = myString1.find(searchFor);

    cout<<"Part 3:"<<endl;
   
    if(position != -1)
    {
        cout<<"The string \""<<searchFor<<"\" was found at: "<<position<<endl;
    }
    else
    {
        cout<<"The string \""<<searchFor<<"\" wasn't found!"<<endl;
    }

    return 0;
}



Όπως βλέπετε, η επεξεργασία/χρήση των string πλέον γίνεται παιχνιδάκι.
Περισσότερα για την κλάση string και τις συναρτήσεις που έχει μπορείτε να βρείτε εδώ:
http://www.cplusplus.com/reference/string/string/

Κάπου εδώ τελειώνει η... μίνι-σειρά για την εισαγωγή στη C++.
Αν θέλετε περισσότερη βοήθεια επισκεφτείτε το παρακάτω link: http://lmgtfy.com/?q=C%2B%2B+tutorial

Αν παρ' όλα αυτά λατρέψατε τον εξαιρετικό τρόπο που τα εξηγώ και θέλετε ντε και καλά να συνεχίσω, κάντε κάνα σχόλιο και ίσως το κάνω (αφού το παίξω δύσκολος εννοείται πρώτα :P )

2 comments:

  1. ελα σε παρακαλωωωω....!!!
    Χωρις πλακα πάντως τα λες πολύ ξεκάθαρα και περιεκτικα. Thanks!!!

    ReplyDelete