Kullanıcı Tanımlı Tür Dönüşümleri (C++)
Dönüştürme, farklı türde bir değerden yeni bir tür değeri üretir. Standart dönüştürmeler C++ dilinde yerleşik olarak bulunur ve yerleşik türlerini destekler ve kullanıcı tanımlı türlerden veya türler arasında dönüştürme gerçekleştirmek için kullanıcı tanımlı dönüştürmeler oluşturabilirsiniz.
Standart dönüştürmeler yerleşik türler arasında, işaretçiler arasında veya devralma ile ilgili türlere başvurular arasında, geçersiz işaretçilere ve null işaretçiye dönüştürmeler yapar. Daha fazla bilgi için bkz . Standart Dönüştürmeler. Kullanıcı tanımlı dönüştürmeler, kullanıcı tanımlı türler arasında veya kullanıcı tanımlı türler ile yerleşik türler arasında dönüştürmeler gerçekleştirir. Bunları Dönüştürme oluşturucuları veya Dönüştürme işlevleri olarak uygulayabilirsiniz.
Dönüştürmeler, programcı bir türe dönüştürme veya doğrudan başlatmada olduğu gibi başka bir türe dönüştürülmesi için çağırdığında veya örtük olduğunda, dil veya program programcı tarafından verilenden farklı bir tür için çağırdığında açık olabilir.
Örtük dönüştürmeler şu durumlarda deneniyor:
bir işleve sağlanan bağımsız değişken, eşleşen parametreyle aynı türe sahip değil.
İşlevden döndürülen değer, işlev dönüş türüyle aynı türe sahip değil.
Başlatıcı ifadesi, başlatmakta olduğu nesneyle aynı türe sahip değil.
Bir koşullu deyimi, döngü yapısını veya anahtarı denetleyen bir ifade, bunu denetlemek için gereken sonuç türüne sahip değildir.
bir işleç için sağlanan işlenen, eşleşen işlenen-parametresiyle aynı türe sahip değil. Yerleşik işleçler için her iki işlenen de aynı türe sahip olmalıdır ve her ikisini de temsil eden ortak bir türe dönüştürülür. Daha fazla bilgi için bkz . Standart Dönüştürmeler. Kullanıcı tanımlı işleçler için, her işlenen eşleşen işlenen-parametresiyle aynı türe sahip olmalıdır.
Standart dönüştürmelerden biri örtük dönüştürmeyi tamamlayamazsa, derleyici kullanıcı tanımlı bir dönüştürmeyi kullanabilir ve bunu tamamlamak için isteğe bağlı olarak ek bir standart dönüştürme kullanabilir.
Aynı dönüştürmeyi gerçekleştiren iki veya daha fazla kullanıcı tanımlı dönüştürme bir dönüştürme sitesinde kullanılabilir olduğunda, dönüştürmenin belirsiz olduğu söylenir. Derleyici, kullanılabilir dönüştürmelerden hangisini seçmesi gerektiğini belirleyemediğinden bu tür belirsizlikler bir hatadır. Ancak, kullanılabilir dönüştürmeler kümesi kaynak kodun farklı konumlarında (örneğin, bir kaynak dosyaya hangi üst bilgi dosyalarının eklendiğine bağlı olarak) farklı olabileceğinden, aynı dönüştürmeyi gerçekleştirmenin birden çok yolunu tanımlamak bir hata değildir. Dönüştürme sitesinde yalnızca bir dönüştürme kullanılabildiği sürece belirsizlik olmaz. Belirsiz dönüştürmelerin ortaya çıkabilmesinin çeşitli yolları vardır, ancak en yaygın olanlar şunlardır:
Birden çok devralma. Dönüştürme birden fazla temel sınıfta tanımlanır.
Belirsiz işlev çağrısı. Dönüştürme, hedef türün bir dönüştürme oluşturucu ve kaynak türünün dönüştürme işlevi olarak tanımlanır. Daha fazla bilgi için bkz . Dönüştürme işlevleri.
Bir belirsizliği genellikle yalnızca ilgili türün adını daha tam olarak niteleyerek veya amacınızı netleştirmek için açık bir atama gerçekleştirerek çözebilirsiniz.
Hem dönüştürme oluşturucuları hem de dönüştürme işlevleri üye erişim denetimi kurallarına uyar, ancak dönüştürmelerin erişilebilirliği yalnızca belirsiz bir dönüştürme belirlenebiliyorsa ve belirlendiğinde dikkate alınır. Bu, rakip dönüştürmenin erişim düzeyi kullanılmasını engellese bile dönüştürmenin belirsiz olabileceği anlamına gelir. Üye erişilebilirliği hakkında daha fazla bilgi için bkz . Üye Erişim Denetimi.
Açık anahtar sözcük ve örtük dönüştürmeyle ilgili sorunlar
Varsayılan olarak, kullanıcı tanımlı bir dönüştürme oluşturduğunuzda, derleyici bunu örtük dönüştürmeler gerçekleştirmek için kullanabilir. Bazen istediğiniz budur, ancak diğer zamanlarda derleyiciye örtük dönüştürmeler yaparken yol gösteren basit kurallar, bunu istemediğiniz kodu kabul etmeye yönlendirebilir.
Sorunlara neden olabilecek örtük dönüştürmenin iyi bilinen örneklerinden biri dönüştürmedir bool
. Boole bağlamında kullanılabilecek bir sınıf türü oluşturmak istemeniz için birçok neden vardır( örneğin, bir if
deyimi veya döngünün denetlenmesi için kullanılabilir), ancak derleyici yerleşik bir türe kullanıcı tanımlı bir dönüştürme gerçekleştirdiğinde, derleyicinin daha sonra ek bir standart dönüştürme uygulamasına izin verilir. Bu ek standart dönüştürmenin amacı, 'den short
'a int
yükseltme gibi işlemlere izin vermektir, ancak sınıf türünüzün hiç hedeflemeyeceğiniz tamsayı bağlamlarında kullanılmasına olanak sağlayan, örneğin, öğesinden bool
'e int
kadar daha az belirgin dönüştürmeler için de kapıyı açar. Bu sorun Güvenli Bool Sorunu olarak bilinir. Bu tür bir sorun, anahtar sözcüğün explicit
yardımcı olabileceği yerdir.
explicit
anahtar sözcüğü derleyiciye, belirtilen dönüştürmenin örtük dönüştürmeleri gerçekleştirmek için kullanılamadığını söyler. Anahtar sözcük kullanılmadan önce explicit
örtük dönüştürmelerin söz dizimsel kolaylığını istiyorsanız, örtük dönüştürmenin bazen oluşturduğu istenmeyen sonuçları kabul etmek veya geçici bir çözüm olarak daha az kullanışlı, adlandırılmış dönüştürme işlevlerini kullanmanız gerekir. Artık anahtar sözcüğünü explicit
kullanarak yalnızca açık atamalar veya doğrudan başlatma gerçekleştirmek için kullanılabilecek ve Güvenli Bool Sorunu tarafından örneklenecek sorun türlerine yol açmayacak kullanışlı dönüştürmeler oluşturabilirsiniz.
anahtar explicit
sözcüğü C++98'den bu yana dönüştürme oluşturucularına ve C++11'den bu yana dönüştürme işlevlerine uygulanabilir. Aşağıdaki bölümlerde anahtar sözcüğün explicit
nasıl kullanılacağı hakkında daha fazla bilgi yer almaktadır.
Dönüştürme oluşturucuları
Dönüştürme oluşturucuları, kullanıcı tanımlı veya yerleşik türlerden kullanıcı tanımlı bir türe dönüştürmeleri tanımlar. Aşağıdaki örnek, yerleşik türünden kullanıcı tanımlı Money
bir türe double
dönüştüren bir dönüştürme oluşturucuyu gösterir.
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance.amount << std::endl;
}
int main(int argc, char* argv[])
{
Money payable{ 79.99 };
display_balance(payable);
display_balance(49.95);
display_balance(9.99f);
return 0;
}
türünde bir bağımsız değişken alan işlevine display_balance
yapılan ilk çağrının Money
, bağımsız değişkeni doğru türde olduğundan dönüştürme gerektirmediğini fark edin. Ancak, değerine sahip bağımsız değişkeninin double
türü işlevin beklediği gibi olmadığından, ikinci çağrısında display_balance
bir 49.95
dönüştürme gerekir. İşlev bu değeri doğrudan kullanamaz, ancak bağımsız değişkenindouble
türünden eşleşen parametreninMoney
türüne dönüştürme olduğundan bağımsız değişkenden türdeki geçici bir değer Money
oluşturulur ve işlev çağrısını tamamlamak için kullanılır. öğesine yapılan üçüncü çağrıda display_balance
bağımsız değişkenin bir double
değil, değeri olan bir float
9.99
olduğuna ve derleyici standart bir dönüştürme gerçekleştirebildiğinden işlev çağrısının yine de tamamlanabilir olduğuna dikkat edin. Bu durumda derleyici, ile arasında standart bir dönüştürme gerçekleştirebilir ve ardından gerekli dönüştürmeyi tamamlamak için kullanıcı tanımlı dönüştürmeyi 'den double
float
double
'a Money
gerçekleştirebilir.
Dönüştürme oluşturucuları bildirme
Aşağıdaki kurallar bir dönüştürme oluşturucuyu bildirmek için geçerlidir:
Dönüştürmenin hedef türü, oluşturmakta olan kullanıcı tanımlı türdür.
Dönüştürme oluşturucuları genellikle kaynak türünden tam olarak bir bağımsız değişken alır. Ancak, her ek parametrenin varsayılan değeri varsa, bir dönüştürme oluşturucu ek parametreler belirtebilir. Kaynak türü, ilk parametrenin türü olarak kalır.
Dönüştürme oluşturucuları, tüm oluşturucular gibi bir dönüş türü belirtmez. Bildirimde bir dönüş türü belirtmek bir hatadır.
Dönüştürme oluşturucuları açık olabilir.
Açık dönüştürme oluşturucuları
Bir dönüştürme oluşturucu explicit
olarak bildirilerek, yalnızca bir nesnenin doğrudan başlatılmasını gerçekleştirmek veya açık bir atama gerçekleştirmek için kullanılabilir. Bu, sınıf türünün bağımsız değişkenini kabul eden işlevlerin de dönüştürme oluşturucusunun kaynak türünün bağımsız değişkenlerini örtük olarak kabul etmesini engeller ve sınıf türünün kaynak türündeki bir değerden kopya olarak başlatılmasını önler. Aşağıdaki örnekte, açık dönüştürme oluşturucunun nasıl tanımlanacağı ve hangi kodun iyi biçimlendirilmiş olduğu üzerindeki etkisi gösterilmektedir.
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
explicit Money(double _amount) : amount{ _amount } {};
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance.amount << std::endl;
}
int main(int argc, char* argv[])
{
Money payable{ 79.99 }; // Legal: direct initialization is explicit.
display_balance(payable); // Legal: no conversion required
display_balance(49.95); // Error: no suitable conversion exists to convert from double to Money.
display_balance((Money)9.99f); // Legal: explicit cast to Money
return 0;
}
Bu örnekte, öğesinin doğrudan başlatılmasını payable
gerçekleştirmek için yine de açık dönüştürme oluşturucuyu kullanabileceğinize dikkat edin. Bunun yerine öğesini kopyalayıp başlatmanız Money payable = 79.99;
bir hata olabilir. bağımsız değişken doğru tür olduğundan ilk çağrısı display_balance
etkilenmez. İkinci çağrısı display_balance
bir hatadır, çünkü dönüştürme oluşturucu örtük dönüştürmeler gerçekleştirmek için kullanılamaz. için üçüncü çağrı display_balance
, için açık atama Money
nedeniyle yasaldır, ancak derleyicinin 'den float
örtük bir atama ekleyerek atamanın tamamlanmasına double
hala yardımcı olduğuna dikkat edin.
Örtük dönüştürmelere izin vermenin kolaylığı cazip olsa da, bunu yapmak zor bulunan hatalar ortaya çıkarabilir. Temel kural, belirli bir dönüştürmenin örtük olarak gerçekleşmesini istediğinizden emin olmanız dışında tüm dönüştürme oluşturucularını açık hale getirmektir.
Dönüştürme işlevleri
Dönüştürme işlevleri, kullanıcı tanımlı bir türden diğer türlere dönüştürmeleri tanımlar. Bu işlevler bazen "atama işleçleri" olarak adlandırılır çünkü bir değer farklı bir türe atandığında dönüştürme oluşturucularıyla birlikte çağrılır. Aşağıdaki örnekte, kullanıcı tanımlı türünden yerleşik bir türe Money
double
dönüştüren bir dönüştürme işlevi gösterilmektedir:
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
operator double() const { return amount; }
private:
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << balance << std::endl;
}
Üye değişkeninin amount
özel olduğuna ve yalnızca değerini döndürmek için türe double
genel dönüştürme işlevinin amount
sunulduğuna dikkat edin. işlevinde display_balance
, değeri balance
akış ekleme işleci <<
kullanılarak standart çıkışa aktarıldığında örtük dönüştürme gerçekleşir. Kullanıcı tanımlı türü Money
için hiçbir akış ekleme işleci tanımlanmadığından, ancak yerleşik türü double
için bir işleç olduğundan, derleyici dönüştürme işlevini Money
double
kullanarak akış ekleme işlecini karşılayabilir.
Dönüştürme işlevleri türetilmiş sınıflar tarafından devralınır. Türetilmiş bir sınıftaki dönüştürme işlevleri yalnızca devralınan dönüştürme işlevini tam olarak aynı türe dönüştürdüğünde geçersiz kılar. Örneğin, türetilmiş sınıf işleci int kullanıcı tanımlı dönüştürme işlevi, standart dönüştürmeler ile short
arasında int
bir dönüştürme ilişkisi tanımlasa bile temel sınıf işlecinin kullanıcı tanımlı dönüştürme işlevini geçersiz kılmaz ve hatta etkilemez.
Dönüştürme işlevlerini bildirme
Aşağıdaki kurallar bir dönüştürme işlevini bildirmek için geçerlidir:
Dönüştürmenin hedef türü, dönüştürme işlevinin bildiriminden önce bildirilmelidir. Sınıflar, yapılar, numaralandırmalar ve tür tanımları dönüştürme işlevinin bildirimi içinde bildirilemez.
operator struct String { char string_storage; }() // illegal
Dönüştürme işlevleri bağımsız değişken almaz. Bildirimde herhangi bir parametrenin belirtilmesi bir hatadır.
Dönüştürme işlevleri, dönüştürme işlevinin adıyla belirtilen ve aynı zamanda dönüştürmenin hedef türünün adı olan bir dönüş türüne sahiptir. Bildirimde bir dönüş türü belirtmek bir hatadır.
Dönüştürme işlevleri sanal olabilir.
Dönüştürme işlevleri açık olabilir.
Açık dönüştürme işlevleri
Bir dönüştürme işlevi açık olarak bildirildiğinde, yalnızca açık bir atama gerçekleştirmek için kullanılabilir. Bu, dönüştürme işlevinin hedef türünün bağımsız değişkenini kabul eden işlevlerin de sınıf türünün bağımsız değişkenlerini örtük olarak kabul etmesini engeller ve hedef türün örneklerinin sınıf türündeki bir değerden kopya olarak başlatılmasını önler. Aşağıdaki örnekte, açık dönüştürme işlevinin nasıl tanımlanacağı ve hangi kodun iyi biçimlendirilmiş olduğu üzerindeki etkisi gösterilmektedir.
#include <iostream>
class Money
{
public:
Money() : amount{ 0.0 } {};
Money(double _amount) : amount{ _amount } {};
explicit operator double() const { return amount; }
private:
double amount;
};
void display_balance(const Money balance)
{
std::cout << "The balance is: " << (double)balance << std::endl;
}
Burada dönüştürme işlevi işleci çifti açık hale getirilmiştir ve dönüştürmeyi gerçekleştirmek için işlevde display_balance
türe double
açık bir tür ataması yapılmıştır. Bu atama atlanırsa, derleyici tür Money
için uygun bir akış ekleme işleci <<
bulamaz ve bir hata oluşur.