Aracılığıyla paylaş


Açıkça Varsayılan Haline Getirilen ve Silinen İşlevler

C++11'de varsayılan ve silinen işlevler, özel üye işlevlerinin otomatik olarak oluşturulup oluşturulmadığı üzerinde size açık denetim sağlar. Silinen işlevler, sorunlu tür yükseltmelerinin bağımsız değişkenlerde (özel üye işlevleri, normal üye işlevleri ve üye olmayan işlevler) oluşmasını önlemek için size basit bir dil sağlar ve bu da istenmeyen işlev çağrısına neden olur.

Açıkça varsayılan ve silinen işlevlerin avantajları

C++'da, derleyici kendi türünü bildirmezse, bir tür için varsayılan oluşturucuyu, kopya oluşturucuyu, kopya atama işlecini ve yıkıcıyı otomatik olarak oluşturur. Bu işlevler özel üye işlevleri olarak bilinir ve bunlar C++ dilindeki basit kullanıcı tanımlı türlerin C'deki yapılar gibi davranmasını sağlar. Başka bir ifadeyle, fazladan kodlama çabası olmadan bunları oluşturabilir, kopyalayabilir ve yok edebilirsiniz. C++11, dil için daha fazla semantik sağlar ve taşıma oluşturucuyu ve taşıma atama oluşturucusunu derleyicinin otomatik olarak oluşturabileceği özel üye işlevleri listesine ekler.

Bu, basit türler için kullanışlıdır, ancak karmaşık türler genellikle özel üye işlevlerinden birini veya daha fazlasını kendileri tanımlar ve bu, diğer özel üye işlevlerinin otomatik olarak oluşturulmasını engelleyebilir. Uygulamada:

  • Herhangi bir oluşturucu açıkça bildirilirse, hiçbir varsayılan oluşturucu otomatik olarak oluşturulmaz.

  • Sanal bir yıkıcı açıkça bildirirse, hiçbir varsayılan yıkıcı otomatik olarak oluşturulmaz.

  • Bir taşıma oluşturucu veya taşıma atama işleci açıkça bildirilirse, bu durumda:

    • Hiçbir kopya oluşturucu otomatik olarak oluşturulmaz.

    • Hiçbir kopyalama atama işleci otomatik olarak oluşturulmaz.

  • Bir kopya oluşturucu, kopya atama işleci, taşıma oluşturucu, taşıma atama işleci veya yıkıcı açıkça bildirilirse, bu durumda:

    • Hiçbir taşıma oluşturucu otomatik olarak oluşturulmaz.

    • Taşıma atama işleci otomatik olarak oluşturulmaz.

Dekont

Buna ek olarak, C ++ 11 standardı aşağıdaki ek kuralları belirtir:

  • Kopya oluşturucu veya yıkıcı açıkça bildirilirse, kopya atama işlecinin otomatik oluşturulması kullanım dışı kalır.
  • Kopya atama işleci veya yıkıcı açıkça bildirilirse, kopya oluşturucunun otomatik oluşturulması kullanım dışı kalır.

Her iki durumda da Visual Studio gerekli işlevleri örtük olarak otomatik olarak oluşturmaya devam eder ve varsayılan olarak bir uyarı yaymaz. Visual Studio 2022 sürüm 17.7'den bu yana, C5267 bir uyarı yaymak için etkinleştirilebilir.

Bu kuralların sonuçları, nesne hiyerarşilerine de sızabilir. Örneğin, herhangi bir nedenle bir temel sınıfın, türetilen bir sınıftan çağrılabilen varsayılan bir oluşturucuya (parametre almamış bir public veya protected oluşturucu) sahip olmaması durumunda, ondan türetilen bir sınıf otomatik olarak kendi varsayılan oluşturucusunu oluşturamaz.

Bu kurallar, doğrudan, kullanıcı tanımlı türler ve yaygın C++ deyimleri olması gerekenlerin uygulanmasını karmaşıklaştırabilir; örneğin, kopya oluşturucuyu ve kopya atama işlecini özel olarak bildirerek ve tanımlamayarak kullanıcı tanımlı bir türü kopyalanamaz hale getirir.

struct noncopyable
{
  noncopyable() {};

private:
  noncopyable(const noncopyable&);
  noncopyable& operator=(const noncopyable&);
};

C++11'den önce bu kod parçacığı, kopyalanamayan türlerin idiomatic biçimiydi. Bununla birlikte, birkaç sorunu vardır:

  • Kopya oluşturucunun gizlenmesi için özel olarak bildirilmesi gerekir, ancak hiç bildirildiği için varsayılan oluşturucunun otomatik olarak oluşturulması engellenir. Hiçbir şey yapmaz bile istiyorsanız varsayılan oluşturucuyu açıkça tanımlamanız gerekir.

  • Açıkça tanımlanan varsayılan oluşturucu hiçbir şey yapmaz olsa bile, derleyici bunu önemsiz olarak kabul eder. Bu, otomatik olarak oluşturulan varsayılan oluşturucudan daha az verimlidir ve noncopyable öğesinin gerçek bir POD türü olmasını önler.

  • Kopya oluşturucu ve kopya atama işleci dış koddan gizlense de, noncopyable öğesinin üye işlevleri ve arkadaşları onu görmeye ve çağırmaya devam edebilir. Bunlar bildirildiyse ancak tanımlanmamışsa, onları çağırmak bağlayıcı hatasına neden olur.

  • Bu yaygın olarak kabul edilen bir deyim olsa da, özel üye işlevlerinin otomatik olarak oluşturulmasına yönelik tüm kuralları anlamadığınız sürece amaç net değildir.

C++11'de, kopyalanamayan deyim daha kolay bir şekilde uygulanabilir.

struct noncopyable
{
  noncopyable() =default;
  noncopyable(const noncopyable&) =delete;
  noncopyable& operator=(const noncopyable&) =delete;
};

C++11 öncesi deyimle ilgili sorunların nasıl çözüldüğüne dikkat edin:

  • Varsayılan oluşturucunun oluşturulması, kopya oluşturucu bildirilerek önlenmeye devam eder, ancak bunu açıkça varsayılan haline getirerek geri alabilirsiniz.

  • Açıkça varsayılan özel üye işlevleri yine de önemsiz olarak kabul edilir, bu nedenle performans cezası yoktur ve noncopyable gerçek pod türü olması engellenmez.

  • Kopya oluşturucu ve kopyalama atama işleci geneldir ancak silinir. Silinen bir işlevi tanımlamak veya çağırmak bir derleme zamanı hatasıdır.

  • Amaç, =default ve =delete'i anlayan herkes için açıktır. Özel üye işlevlerinin otomatik olarak oluşturulmasına yönelik kuralları anlamanız gerekmez.

Taşınamaz, yalnızca dinamik olarak ayrılabilen veya dinamik olarak ayrılamayan kullanıcı tanımlı türler oluşturmak için benzer deyimler vardır. Bu deyimlerin her biri, C++11 öncesi, benzer sorunlarla karşılaşan ve C++11'de bunlara varsayılan olarak uygulanan ve silinen özel üye işlevleriyle benzer şekilde çözümlenen uygulamalara sahiptir.

Açıkça varsayılan işlevler

Özel üye işlevlerinden herhangi birini varsayılan olarak ayarlayabilirsiniz; özel üye işlevinin varsayılan uygulamayı kullandığını açıkça belirtmek, özel üye işlevini abonelik dışı erişim niteleyicisi ile tanımlamak veya otomatik oluşturması diğer koşullar tarafından engellenen özel üye işlevini yeniden devreye almak için.

Özel bir üye işlevini şu örnekte olduğu gibi bildirerek varsayılan olarak kullanırsınız:

struct widget
{
  widget()=default;

  inline widget& operator=(const widget&);
};

inline widget& widget::operator=(const widget&) =default;

Bir sınıfın gövdesinin dışında bir özel üye işlevini,linamaz olduğu sürece varsayılan olarak kullanabileceğinize dikkat edin.

Önemsiz özel üye işlevlerinin performans avantajları nedeniyle, varsayılan davranışı istediğinizde boş işlev gövdeleri yerine otomatik olarak oluşturulan özel üye işlevlerini tercih kullanmanızı öneririz. Bunu, özel üye işlevini açıkça varsayılan olarak belirterek veya bildirmeyerek (ve otomatik olarak oluşturulmasını engelleyecek diğer özel üye işlevlerini bildirmeyerek) yapabilirsiniz.

Silinen işlevler

Tanımlanmalarını veya çağrılmalarını önlemek için özel üye işlevlerini ve normal üye işlevlerini ve üye olmayan işlevleri silebilirsiniz. Özel üye işlevlerinin silinmesi, derleyicinin istemediğiniz özel üye işlevleri oluşturmasını önlemenin daha temiz bir yolunu sağlar. İşlev bildirildiği gibi silinmelidir; daha sonra bir işlevin bildirilebileceği ve daha sonra varsayılan olarak ayarlandığı şekilde silinemez.

struct widget
{
  // deleted operator new prevents widget from being dynamically allocated.
  void* operator new(std::size_t) = delete;
};

Normal üye işlevinin veya üye olmayan işlevlerin silinmesi, sorunlu tür yükseltmelerinin istenmeyen bir işlevin çağrılmasına neden olmasını önler. Silinen işlevler aşırı yükleme çözümlemesine katılmaya devam ettiğinden ve türler yükseltildikten sonra çağrılabilecek işlevden daha iyi bir eşleşme sağladığından bu işe yarar. İşlev çağrısı, daha belirgin, ancak silinmiş bir işleve çözümlenir ve bir derleyici hatasına neden olur.

// deleted overload prevents call through type promotion of float to double from succeeding.
void call_with_true_double_only(float) =delete;
void call_with_true_double_only(double param) { return; }

Önceki örnekte, bağımsız float değişken kullanarak çağırmanın call_with_true_double_only derleyici hatasına neden olduğuna, ancak bağımsız int değişken kullanarak çağrılmayacağına call_with_true_double_only dikkat edin; int bu durumda, bağımsız değişken, istediğiniz gibi olmasa bile işlevin intdouble sürümünden yükseltilir ve başarılı bir şekilde çağrılır.double Çift olmayan bağımsız değişken kullanarak bu işleve yapılan herhangi bir çağrının derleyici hatasına neden olduğundan emin olmak için, silinen işlevin şablon sürümünü bildirebilirsiniz.

template < typename T >
void call_with_true_double_only(T) =delete; //prevent call through type promotion of any T to double from succeeding.

void call_with_true_double_only(double param) { return; } // also define for const double, double&, etc. as needed.