Bu modülde, C++'ta farklı cast türlerini öğrenerek tür dönüşümlerinin nasıl çalıştığını keşfedeceksiniz. static_cast, dynamic_cast ve reinterpret_cast gibi dönüşüm operatörlerini kullanarak veri türleri arasında güvenli ve etkili geçişler yapmayı öğreneceksiniz. Ayrıca, dynamic_cast ile polymorphism kavramını daha iyi anlayarak, türetilmiş sınıflar arasında güvenli dönüşümler gerçekleştireceksiniz.
- Ex00 - Conversion of scalar types
- Ex01 - Serialization
- Ex02 - Identify real type
- Tür Dönüştürme Operatörleri Tablo
Bu egzersizde, C++'ta bir sınıf ScalarConverter oluşturacak ve bu sınıfın yalnızca bir statik metodunu yazacaksınız: convert. Bu metod, bir C++ literal'inin string temsilini alacak ve şu dört scalar türüne dönüştürecektir:
charintfloatdouble
-
Sınıf Özellikleri:
ScalarConvertersınıfı instansiyalanamaz (kullanıcılar tarafından örneği oluşturulamaz). Bu, yalnızcastatikmetod convert kullanılarak işlem yapılması gerektiği anlamına gelir.
-
Metod:
convertstatikmetodu,stringparametre olarak bir C++ literal’ini alacak ve bu değeri uygun türlerdeki değerlere dönüştürecektir.
-
Dönüşüm Türleri:
-
char: Verilen değerin karakter türüne dönüştürülmesi. -
int: Verilen değerin tam sayı türüne dönüştürülmesi. -
float: Verilen değerin kayan nokta türüne (float) dönüştürülmesi. -
double: Verilen değerin çift hassasiyetli kayan nokta türüne (double) dönüştürülmesi.
-
-
Özel Durumlar:
-
Char için: Geçerli olmayan karakterler (görüntülenebilir olmayanlar) için bir hata mesajı gösterilecek.
-
Pseudo literals:
-inff,+inff,nanfgibi değerler de işlenmeli. -
Dönüşüm Hataları: Eğer dönüşüm mümkün değilse veya taşma durumu söz konusuysa, kullanıcıya bilgi veren bir mesaj yazdırılmalıdır.
-
-
İzin verilen Fonksiyonlar:
- String'den bir sayıya dönüşüm yapan her türlü fonksiyon kullanılabilir (örneğin,
std::stoi,std::stof,std::stod).
- String'den bir sayıya dönüşüm yapan her türlü fonksiyon kullanılabilir (örneğin,
-
Test Programı:
-
ScalarConvertersınıfını test etmek için bir program yazılacaktır. Bu programda, kullanıcıdan alınan input, uygun türe dönüştürülecek ve her bir türdeki değer ekrana yazdırılacaktır. -
./convert 0 char: Non displayable int: 0 float: 0.0f double: 0.0 ./convert nan char: impossible int: impossible float: nanf double: nan ./convert 42.0f char: '*' int: 42 float: 42.0f double: 42.0
-
static_cast C++ dilinde kullanılan bir tür dönüşüm operatörüdür. Derleme zamanında, türler arasında güvenli dönüşümler yapılmasını sağlar ve özellikle temel türler arasında dönüşümde kullanılır. static_cast, derleyici tarafından kontrol edilen ve hatasız dönüşümler sağlar, fakat yanlış bir dönüşüm yapılması durumunda derleme hatası alınır.
Temel Özellikler:
-
Derleme Zamanı Dönüşümü:
static_cast, dönüşüm işlemlerini derleme zamanında gerçekleştirir. Bu, dönüşümün tür güvenliğini sağlamak anlamına gelir ve hata yapma olasılığını azaltır. -
Basit Dönüşümler: Genellikle türler arasındaki temel dönüşümleri (örneğin,
int'tenfloat'a,double'danint'e) yapmak için kullanılır. -
Veri Kaybı: Dönüşüm sırasında veri kaybı yaşanabilir (örneğin,
double'ıint'e dönüştürmek). Bu nedenle dikkatli kullanmak gerekir.
Kullanım Alanları:
-
Temel Veri Türleri:
static_cast, genelliklecharveintgibi temel veri türleri arasında dönüşüm yapmak için kullanılır. -
Sınıflar Arasında Dönüşüm: Aynı sınıf hiyerarşisine sahip nesneler arasında dönüşüm yapmak için de kullanılabilir. Ancak, bu durumda daha dikkatli olmak gerekmektedir.
Örnek Kullanım:
-
char ScalarConverter::toChar(int value) { // int türündeki 'value' değişkenini char türüne dönüştür char c = static_cast<char>(value); // Eğer değer char aralığının dışındaysa, dönüştürme mümkün değil if (value > numeric_limits<char>::max() || value < numeric_limits<char>::min()) cout << "char: impossible" << endl; // Eğer karakter ekranda gösterilebilir bir karakter değilse else if (!isDisplayableChar(c)) cout << "char: Non displayable" << endl; // Geçerli ve gösterilebilir bir karakterse, ekrana yazdır else cout << "char: '" << c << "'" << endl; return c; }
C++'da şablonlar (templates), farklı veri türleriyle çalışabilen genel (generic) fonksiyonlar veya sınıflar yazmanızı sağlayan bir özelliktir. Şablonlar, kodunuzu daha esnek ve yeniden kullanılabilir hale getirir. Örneğin, aynı işlemi int, double, string gibi farklı türlerle yapmanız gerekiyorsa, her tür için ayrı fonksiyonlar yazmak yerine tek bir şablon kullanabilirsiniz. Şablonlarla ilgili daha detaylı bilgi için Cpp 07 repoma göz atabilirsiniz.
Bu iki kavram, bilgisayar bilimlerinde IEEE 754 (kayan noktalı sayıların (floating-point numbers) bilgisayarlarda nasıl temsil edileceğini belirleyen uluslararası bir standarttır.) standardına dayalı kayan noktalı sayıların temsilinde kullanılır.
1. NaN (Not a Number)
- Tanım: Matematiksel olarak tanımsız işlemlerin sonucudur.
- Oluşma Durumları:
0.0 / 0.0(Sıfırın sıfıra bölünmesi)sqrt(-1)(Negatif bir sayının karekökü)log(-1)(Negatif bir sayının logaritması)inf - inf(İki sonsuzun farkı)
- Özellikler:
NaNeşitlik karşılaştırmalarında bile kendisiyle eşit değildir(nan == nan → false).IEEE 754standardı, NaN değerinin belirli bir bit deseniyle temsil edilmesini sağlar.
2. Infinity (∞ - Sonsuzluk)
- Tanım: Çok büyük veya çok küçük sayıların matematiksel limiti aştığında ortaya çıkar.
- Oluşma Durumları:
pozitif_sayı / 0.0 → +∞(pozitif sonsuz)negatif_sayı / 0.0 → -∞(negatif sonsuz)exp(1000)gibi büyük bir hesaplama (overflow durumu)
- Özellikler:
- Sonsuzluk, aritmetik işlemlerle taşma (overflow) yaşandığında ortaya çıkar.
inf + 1 == infveyainf * 2 == infgibi işlemler yine sonsuzluk üretir.1.0 / inf == 0.0olur.
NaN ve Inf İçin Kod İçindeki Kullanım
- NaN değerleri için
"nan","-nan","nanf","-nanf"gibi girdiler kontrol ediliyor ve"impossible"çıktısı veriliyor. - Infinity (sonsuzluk) değerleri için
"inf","-inf","inff","-inff"girdileri işlenerekfloatvedoubleçıktıları basılıyor.
numeric_limits, sayı türlerinin sınırlarını sorgulamak için kullanılan bir şablon sınıfıdır. Bu sınıf, bir türün alabileceği en küçük ve en büyük değeri, hassasiyet gibi özellikleri verir. Özellikle sayısal hesaplamalarda taşma (overflow) veya hassasiyet kontrolü için kullanılır.
Temel Özellikleri:
-
MinimumveMaksimumDeğerler: numeric_limits sınıfı, belirli bir türün alabileceği en küçük ve en büyük değerleri döndüren metotlar sunar. -
std::numeric_limits<T>::min()— Türün alabileceği en küçük değeri verir. -
std::numeric_limits<T>::max()— Türün alabileceği en büyük değeri verir. -
Epsilon: Sayısal hassasiyetle ilgili bir değer sağlar. Örneğin,
std::numeric_limits<float>::epsilon()fonksiyonu, float türünün en küçük pozitif farkını verir. -
Düzenlilik ve Taşma: Sayısal türlerin taşma (overflow) ve sıfırla bölme gibi özel durumlar için özelliklere de erişilebilir.
Örnek Kullanım:
#include <iostream>
#include <limits>
int main() {
std::cout << "int min: " << std::numeric_limits<int>::min() << std::endl;
std::cout << "int max: " << std::numeric_limits<int>::max() << std::endl;
std::cout << "float epsilon: " << std::numeric_limits<float>::epsilon() << std::endl;
return 0;
}
Bu alıştırma (Exercise 01), serileştirme (serialization) ve ters serileştirme (deserialization) işlemlerini gerçekleştiren bir Serializer sınıfı yazmanızı istiyor. Ayrıca, bu sınıfın doğru çalıştığını test eden bir program yazmanız gerekiyor.
İşte adım adım ne yapmanız gerektiği:
1. Serializer Sınıfını Oluştur
-
Serializersınıfı, kullanıcı tarafından hiçbir şekilde örneklenemez (initializable) olmamalıdır. Yani, sınıfın constructor'ları private olmalıdır. -
Sınıf, iki
staticmetoda sahip olmalıdır:-
uintptr_t serialize(Data* ptr);: BirData pointer'ını alır ve onuuintptr_ttürüne (işaretsiz bir tamsayı türü) dönüştürür. -
Data* deserialize(uintptr_t raw);: Biruintptr_ttüründeki değeri alır ve onuData*türüne (yani bir Data pointer'ına) dönüştürür.
-
2. Data Yapısını Oluştur
-
Dataadında bir yapı (struct) oluşturun. Bu yapı boş olmamalıdır, yani en az bir veri üyesine (data member) sahip olmalıdır. Örneğin: -
struct Data { int a; double b; };
3. Test Programını Yaz
-
mainfonksiyonunda aşağıdaki adımları takip edin:-
Bir
Datanesnesi oluşturun ve içine veri atayın. -
Bu nesnenin adresini
serialize()fonksiyonuna gönderin ve dönenuintptr_tdeğerini kaydedin. -
Bu
uintptr_tdeğerinideserialize()fonksiyonuna gönderin ve dönenData*pointer'ını kaydedin. -
Orijinal pointer ile
deserialize()sonucu dönen pointer'ın aynı adresi gösterdiğini kontrol edin. -
Data nesnesinin veri üyelerini ekrana yazdırarak doğru çalıştığını doğrulayın.
-
static metodlar, belirli bir sınıfın üyesi olan ancak o sınıfın belirli bir nesnesine bağlı olmayan metodlardır. Yani, static metodlar nesne oluşturmadan çağrılabilir. Bunun sebebi, static metodların sınıf düzeyinde tanımlanması ve sınıfın herhangi bir örneğine (instance) bağlı olmamasıdır.
-
Nesneye Bağlı Değildir:
Static metodlar, sınıfa ait olup belirli bir nesneye ihtiyaç duymaz. -
Sadece Static Üyelere Erişebilir:
Static metodlar, sınıf içindeki yalnızcastaticdeğişkenlere ve diğer static metodlara erişebilir. -
Sınıf Adı ile Çağrılır: Bir nesne oluşturmadan, doğrudan sınıf adı üzerinden çağrılır:
-
SinifAdi::MetodAdi();
-
-
this Pointer'ı Kullanamaz:
Static metodlarnesneye bağlı olmadığı içinthispointer'ı içermez.
reinterpret_cast, bir tür dönüşüm (type casting) operatörüdür. Bu operatör, bir türü tamamen farklı bir türe dönüştürmek için kullanılır. Ancak, diğer tür dönüşüm operatörlerinden (örneğin, static_cast, dynamic_cast, const_cast) farklı olarak, reinterpret_cast çok daha güçlü ve tehlikeli bir operatördür. Çünkü bu operatör, derleyiciye "bu veriyi başka bir tür olarak yorumla" der ve herhangi bir tür kontrolü yapmaz.
reinterpret_cast aşağıdaki durumlarda kullanılır:
-
Pointer Türleri Arasında Dönüşüm: Bir pointer'ı başka bir pointer türüne dönüştürmek.
- Örneğin,
int*türündeki bir pointer'ıdouble*türüne dönüştürmek.
- Örneğin,
-
Pointer ile Tamsayı Arasında Dönüşüm: Bir pointer'ı tamsayıya veya bir tamsayıyı pointer'a dönüştürmek.
- Örneğin, bir pointer'ın bellek adresini
uintptr_ttüründe bir tamsayıya çevirmek.
- Örneğin, bir pointer'ın bellek adresini
-
Farklı Türler Arasında Dönüşüm: İlişkisiz türler arasında dönüşüm yapmak.
- Örneğin, bir sınıf pointer'ını tamamen farklı bir sınıf pointer'ına dönüştürmek.
Nasıl Kullanılır?
reinterpret_cast'in genel kullanım şekli şöyledir:
reinterpret_cast<YeniTür>(ifade);-
YeniTür: Dönüştürmek istediğiniz tür.
-
ifade: Dönüştürülecek değer veya ifade.
uintptr_t, bir tamsayı türüdür ve işaretçileri (pointer) tutabilecek kadar büyük bir tamsayı türü olarak tanımlanır. Bu tür, <cstdint> veya <stdint.h> başlık dosyasında tanımlıdır.
-
Taşınabilirlik sağlar:
uintptr_t, platform bağımsız bir şekilde, işaretçileri tamsayıya çevirmek için kullanılır. -
İşaretçi genişliği ile aynıdır:
uintptr_t, kullanılan sistemin işaretçi boyutuna eşittir. Örneğin:32-bitsistemdeuintptr_t32 bitolur.64-bitsistemdeuintptr_t64 bitolur.
-
İşaretçi dönüşümlerinde güvenli kullanılır: İşaretçiyi tamsayıya çevirmek ve tekrar işaretçiye dönüştürmek için kullanılır.
Bu alıştırmada, yalnızca genel (public) sanal bir yıkıcıya (destructor) sahip olan bir Base sınıfı oluşturmalısınız. Ayrıca, Base sınıfından türeyen A, B ve C adında üç boş sınıf oluşturmalısınız. Bu dört sınıfın Ortodoks Kanonik Form'da (Orthodox Canonical Form) olması gerekmez.
Aşağıdaki işlevleri uygulamalısınız:
-
Base * generate(void);A,BveyaCtüründen bir nesneyi rastgele oluşturmalı ve bunuBasetüründe bir işaretçi olarak döndürmelidir. Rastgele seçim için istediğiniz yöntemi kullanabilirsiniz.
-
void identify(Base* p);ptarafından işaret edilen nesnenin gerçek türünü yazdırmalıdır ("A","B"veya"C").
-
void identify(Base& p);pdeğişkeninin işaret ettiği nesnenin gerçek türünü yazdırmalıdır ("A","B"veya"C"). Ancak, bu fonksiyonun içinde işaretçi (pointer) kullanmak yasaktır.
Yasaklı Fonksiyonlar ve Kütüphaneler
std::typeinfokütüphanesi kullanılamaz.typeidoperatörü yasaktır.
dynamic_cast, C++ dilinde çalışma zamanında (runtime) türetilmiş bir sınıfın gerçek türünü belirlemek için kullanılan bir cast operatörüdür. Özellikle polimorfizm (çok biçimlilik) kullanıldığında, bir Base sınıf işaretçisini veya referansını türetilmiş sınıflara güvenli bir şekilde dönüştürmek için kullanılır.
Base *basePtr = new A();
A *aPtr = dynamic_cast<A*>(basePtr);
if (aPtr)
std::cout << "Nesne A türündedir." << std::endl;
else
std::cout << "Nesne A türünde değildir." << std::endl;dynamic_cast, yalnızca en az bir sanal (virtual) fonksiyona sahip sınıflarda çalışır. Bu yüzdenBasesınıfının en az bir sanal fonksiyona sahip olması gerekir.- Eğer
dynamic_castbaşarısız olursa:- İşaretçi (
pointer) kullanılıyorsa,nullptrdöndürülür. - Referans (
reference) kullanılıyorsa,std::bad_castistisnası (exception) fırlatılır.
- İşaretçi (
Bu alıştırmada, dynamic_cast kullanarak Base türündeki bir işaretçi ve referansı A, B ve C türüne dönüştürüp, tür belirleme işlemi yapmanız gerekmektedir.
| Operatör | Kullanım Amacı | Örnek Kullanım | Ek Açıklamalar |
|---|---|---|---|
static_cast |
İlişkili türler arasında güvenli dönüşüm yapar | int a = static_cast<int>(3.14); |
Türetilmiş sınıftan temel sınıfa dönüşüm için kullanılır. |
dynamic_cast |
Çalışma zamanında tür kontrolü yapar | Base* b = dynamic_cast<Base*>(derivedPtr); |
Yalnızca polimorfik sınıflar için kullanılır. Başarısız olursa nullptr döner. |
const_cast |
const veya volatile niteliklerini kaldırır |
const int* p; int* q = const_cast<int*>(p); |
const kaldırıldığı için değişken değiştirilebilir hale gelir. |
reinterpret_cast |
Tür güvenliği olmadan dönüşüm yapar | void* ptr = reinterpret_cast<void*>(someInt); |
İşaretçiler ve türler arasında dönüşüm yapar, ancak güvenli değildir. |
2025 This project was created by Derya ACAR.