Defining New Types

Kategori: C++ , 15 Kasım 2019 , JanFranco


C++'da int, float, double, char gibi tipler built-in tiplerdir. İki tip vardır ve diğeri class tiplerdir. Class tiplere örnek olarak string, vector, istream verilebilir. Class tipler, built-in tiplere göre daha karmaşıktır, komplekstir. Kendi gömülü tiplerimizi oluşturamayız fakat yeni class tipleri oluşturabiliriz. Örneğin daha önce Student_info adını koyduğumuz struct'ı ele alalım:


struct Student_info {
    std::string name;
    double midterm, final;
    std::vector<double> homework;
    
    std::istream& read(std::istream&); // added
    double grade() const; // added
};
Burada iki yeni satır ekledik. Verileri okumak için read() fonksiyonu ve grade() fonksiyonu. grade() fonksiyonunu tanımlarken yazdığımız const kelimesi burada data elementlerini (name, midterm, final, homework) değiştirmeyeceğimiz anlamına geliyor. Burada name, midterm gibi değerler data element, fonksiyonlar ise member functions. Sınıftaki fonksiyonlara nokta operatörü ile erişebiliriz. Student_info::read gibi. Bahsetmişken read() fonksiyonunu tanımlayalım:


istream& Student_info::read(istream& in){
    in >> name >> midterm >> final;
    read_hw(in, homework);
    return in;
}
Bu fonksiyondaki önemli noktalara göz atalım. İlk olarak read yerine Student_info::read yazdık. Bu read() fonksiyonunun Student_info'ya ait olduğunu gösteriyor. Bu fonksiyon zaten Student_info'ya ait olduğundan Student_info objesi oluşturmadık veya argüman olarak göndermedik. Eğer Student_info'dan bir obje oluşturup read() fonksiyonunu çağırırsak (s.read(cin)), read() fonksiyonu midterm, final ve homework değişkenleri yerine s.midterm, s.final, s.homework değişkenlerini kullanır. Şimdi grade() member fonksiyonunu oluşturalım:


double Student_info::grade() const{
    return ::grade(midterm, final, homework);
}
Yine grade() yerine Student_info::grade() yazarak bu fonksiyonun Student_info'ya ait olduğunu gösterdik. Burada yine const kelimesini kullandık. Buradaki const kelimesini daha iyi anlayabilmek için aşağıdaki farkı inceleyelim:


double Student_info::grade() const { ... }
double grade(const Student_info&) { ... }
İkinci satırda grade() fonksiyonu herhangi bir sınıfa ait değil. İlk satırda ise bir sınıfa ait olduğundan parametre olarak tekrar Student_info objesi tanımlamak zorunda değiliz. const kelimesi dışında bir de ::grade() fonksiyonunu görüyoruz. Burada :: karakterlerini kullanmasaydık compiler Student_info::grade() fonksiyonunu çağıracaktı ve bu argüman hatasına yol açacaktı. Biz diğer grade() fonksiyonunu çağırmak istiyoruz. Bu nedenle :: karakterlerini koyarak Student_info::grade() fonksiyonundan ayırmamız gerekli.

Burada oluşturduğumuz sınıfta herhangi bir protection bulunmuyor. Burada fonksiyon haricindeki değerlere dışarıdan ulaşılmasını istemiyoruz. Bu durumda public ve private anahtar kelimelerini kullanabiliriz:


class Student_info {
    public:
	double grade() const;
	std::istream& read(std::istream&);
    private:
        std::string name;
        double midterm, final;
        std::vector<double> homework;
};
Burada fonksiyonlara dışarıdan erişilebilmesini (s.grade(), s.read()) public tanımı ile mümkün kıldık. private tanımı ile de name, midterm, final ve homework değişkenlerine dışarıdan erişilmesini kısıtladık. Ayrıca artık Student_info'yu struct olarak değil sınıf olarak (class) tanımladık. Aslında struct ve class kullanım şekilleri ile aynıdır. Birinde yaptığımız işlemi diğerinde de yapabiliriz. Fakat burada Student_info artık basit bilgiler depolamak yerine kompleks işlemlere geçtiğinden class olarak yazmak daha doğru olur. Burada name değişkenine de dışarıdan erişimi engelledik. Fakat kullanıcının name değişkenini okumasını istiyoruz. Bu durumda name değişkenini return eden public fonksiyon yazabiliriz:


class Student_info {
    public:
	double grade() const;
	std::istream& read(std::istream&)
	std::string name() const { return n; } // added
    private:
	std::string n; // changed
	double midterm, final;
	std::vector<double> homework;
};
Burada tanımladığımız name fonksiyonun özel bir adı vardır: accessor function (erişim fonksiyonu). Artık n (name) değişkeni private, name() fonksiyonu public olduğundan compare() fonksiyonunu güncelleyelim:


bool compare(const Student_info& x, const Student_info& y){
    return x.name() < y.name();
}
Sınıfımızla ilgili her şeyi düşündük fakat objenin ilk yaratıldığı anda ne olacağını atladık. Şuanda bir obje oluşturulduğunda değişkenler boş değer alacak. Örneğin vektörümüz varsa boş vektör, stringimiz var ise boş string olacak vs. Başlangıç durumlarını yönetebileceğimiz özel fonksiyonlar mevcut: Constructors. Constructorlar (inşa ediciler) objeyi ilk oluşturduğumuzda, objedeki değişkenlerin değerini atayan bir fonksiyondur. Başlangıçta iki durum olmasını istiyoruz: Herhangi bir şey olmaması veya cin'den okuma olmasını. Yani objeyi aşağıdaki iki şekilde de oluşturabiliriz:


Student_info s;
Student_info s2(cin);
Bu durumda iki constructor oluşturabiliriz:


class Student_info {
    public:
	Student_info() // construct an empty Student_info object
	Student_info(std::istream&); // construct one by reading a stream
    ...
};
Herhangi bir argüman almayan, parametresi olmayan constructor'a default constructor denir. Bir obje oluşturduğumuzda ilk olarak obje için bellekte yer ayırtılır. Daha sonra constructor'un initiazer listesindeki değerler, data element'lere atanır. Daha sonrasında da constructor body'sindeki kodlar çalışır. Aşağıdaki örneği inceleyelim:


Student_info::Student_info(): midterm(0), final(0) { }
Burada midterm ve final değişkenlerinin değeri constructor tarafından 0 olarak belirlendi. Diğer değişkenler (n ve homework) dolaylı olarak initialize edildi. Dolaylı yoldan kasıt n değişkeninin değeri default string constructor tarafından verildi. Homework'ün değeri default vector constructor tarafından verildi. Parametreli constructor'u da kullanalım:


Student_info::Student_info(istream& is) { read(is); }
Artık sınıfı kullanarak obje üretebiliriz:


int main()
{
    vector<Student_info> students;
    Student_info record;
    string::size_type maxlen = 0;

    while (record.read(cin)) { // changed
	maxlen = max(maxlen, record.name().size()) // changed
	students.push_back(record);
    }

    sort(students.begin(), students.end(), compare);

    for (vector<Student_info>::size_type i = 0; i != students.size(); ++i) {
	cout << students[i].name() // changed
	<< string(maxlen + 1 - students[i].name.size(), ' ');
	try {
	    double final_grade = students[i].grade(); // changed
	    streamsize prec = cout.precision();
	    cout << setprecision(3) << final_grade << setprecision(prec) << endl;
	} catch (domain_error e) {
	    cout << e.what() << endl;
	}
    }
    
    return 0;
}


Sonraki Yazı: Managing Memory and Low Level Data Structures
Yorumlar

Henüz bir yorum bulunmuyor.
Yorum bırakın