Sınıf içinde veya dışında işlev bildirimi


91

C ++ öğrenmeye çalışan bir JAVA geliştiricisiyim, ancak standart işlev bildirimleri için en iyi uygulamanın ne olduğunu gerçekten bilmiyorum.

Sınıfta:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

Veya dışarıda:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

İkincisinin daha az okunabilir olduğuna dair bir his var içimde ...


1
Aslında burada 3 seçenek var. İkinci örneğiniz işlev tanımını başlık dosyasında (ancak yine de satır içi değil) veya ayrı bir .cppdosyada olabilir.
Cody Grey

Bu soru anlamanıza yardımcı olabilir.
Björn Pollex

3
Sadece bir not: bildirim her zaman sınıfın içindedir, ancak tanım içeride veya dışarıdadır. Soru başlığı ve gövdesi s / beyana / tanıma / bana inanmıyor musunuz? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev

1
Sınıf içindeki fonksiyon tanımlarından kaçınılmalıdır. Örtük olarak kabul edilirler inline.
John Strood

@JohnStrood yani? inlineyalnızca, başka bir çeviri birimi kullanıyorsa gerekli olan tek tanım kuralını gevşetirClazz
Caleth

Yanıtlar:


57

C ++, yazılım geliştirme için nesne yönelimli paradigmayı desteklemesi açısından nesne yönelimlidir.

Bununla birlikte, Java'dan farklı olarak, C ++ sizi sınıflarda işlev tanımlarını gruplandırmaya zorlamaz: Bir işlevi bildirmenin standart C ++ yolu, herhangi bir sınıf olmadan yalnızca bir işlevi bildirmektir.

Eğer yöntem bildiriminde bahsediyoruz yerine ise / tanım daha sonra standart yolu bir dosyayı (normalde adlı dahil sadece deklarasyon koymaktır .hya .hpp) ve ayrı bir uygulama dosyasında tanımını (normalde adlandırılmış .cppveya .cxx). Bunun gerçekten can sıkıcı olduğuna ve bazı tekrarlar gerektirdiğine katılıyorum, ancak dil böyle tasarlandı.

Hızlı deneyler ve tek dosyalı projeler için her şey işe yarayabilir ... ancak daha büyük projeler için bu ayrım pratik olarak gerekli olan bir şeydir.

Not: Java'yı bilseniz bile, C ++ tamamen farklı bir dildir ... ve deney yapılarak öğrenilemeyecek bir dildir. Bunun nedeni, çok sayıda asimetri ve görünüşte mantıksız seçimler içeren oldukça karmaşık bir dil olmasıdır ve en önemlisi, bir hata yaptığınızda, sizi Java'daki gibi kurtaracak "çalışma zamanı hata melekleri" yoktur ... ama bunun yerine vardır " tanımsız davranış arka plan dosyaları ".

C ++ öğrenmenin tek makul yolu okumaktır ... ne kadar akıllı olursanız olun, komitenin neye karar verdiğini tahmin edemezsiniz (aslında zeki olmak bazen bir sorundur çünkü doğru cevap mantıksızdır ve tarihsel bir sonucudur. miras.)

İyi bir veya iki kitap seçin ve baştan sona okuyun.


7
Birisi Java'dan gelip C ++ konusunda yardım isterse, "bildiğiniz dil bir şeye takıntılı" derseniz bu ona ne söyler? Diğer dillerle karşılaştırması yok, bu yüzden bu ona hemen hemen hiçbir şey söylemiyor. OP'ye pek bir şey anlatmayan, takıntılı gibi güçlü bir şekilde duygusal olarak çağrılan bir kelime kullanmaktan daha iyidir, bu kısmı dışarıda bırakmayı düşünebilirsiniz. Dahası, "her şey için bir sınıf kullan" bağlamı nedir? Java'da, bir yöntem için sınıf kullanmazsınız. Değişken için sınıf kullanmazsınız. Bir dosya için sınıf kullanmıyorsun..Peki burada "her şey" nedir? Rant mı?
Daniel S.

3
@DanielS: Görünüşe göre seni rahatsız ettiği için bu kısmı kaldırdım (neden olduğu hakkında hiçbir fikrim yok). Elbette Java hakkında rant yapmıyorum çünkü aslında Java kullanmıyorum, o zamanlar sadece OOP'nin Nesne Takıntılı Programlama olarak komik bir şaka olduğunu düşündüm, ama görünüşe göre öyle değil. Java 1.1 sertifikalı bir programcıydım ama o zamanlar, herhangi bir nedenle zorlamadıkça, bu "programlama dilini" kullanmayacağıma karar verdim ve şimdiye kadar bundan kaçınmayı başardım.
6502

Teşekkürler, bence şimdi çok daha iyi okuyor. Kırgın görünüyorsam özür dilerim. Bir dahaki sefere daha pozitif olmaya çalışacağım.
Daniel S.

15
Soruya cevap vermiyor
Petr Peller

1
@PetrPeller: Üçüncü paragrafın sizin için net olmayan kısmı nedir?
6502

27

Birincisi üye işlevinizi bir satır içi işlev olarak tanımlar , ikincisi ise tanımlamaz . Bu durumda işlevin tanımı, başlığın kendisinde bulunur.

İkinci uygulama, işlevin tanımını cpp dosyasına yerleştirir.

Her ikisi de anlamsal olarak farklıdır ve bu sadece bir stil meselesi değildir.


2
cplusplus.com/doc/tutorial/classes aynı yanıtı verir: "Bir sınıf üyesi işlevini tamamen kendi sınıfı içinde tanımlamak veya yalnızca prototipi ve daha sonra tanımını dahil etmek arasındaki tek fark, ilk durumda işlevin otomatik olarak derleyici tarafından bir satır içi üye işlevi olarak kabul edilirken, ikincisinde, aslında davranışta hiçbir fark olmadığını varsayan normal (satır içi olmayan) bir sınıf üye işlevi olacaktır. "
Buttons840

18

Fonksiyon tanımı, sınıfın dışında daha iyidir. Bu şekilde, gerekirse kodunuz güvende kalabilir. Başlık dosyası sadece beyanlar vermelidir.

Birinin kodunuzu kullanmak istediğini varsayalım, ona sınıfınızın .h dosyasını ve .obj dosyasını (derlemeden sonra elde edilen) verebilirsiniz. Kodunuzu kullanmak için .cpp dosyasına ihtiyacı yoktur.

Bu şekilde uygulamanızı başka kimse göremez.


10

"Sınıfın içinde" (I) yöntemi, "sınıfın dışında" (O) yöntemiyle aynı şeyi yapar.

Ancak, bir sınıf yalnızca bir dosyada (.cpp dosyası içinde) kullanıldığında (I) kullanılabilir. (O) bir başlık dosyasındayken kullanılır. cpp dosyaları her zaman derlenir. #İnclude "header.h" kullandığınızda başlık dosyaları derlenir.

Bir başlık dosyasında (I) kullanırsanız, (Fun1) işlevi, #include "başlık.h" öğesini her eklediğinizde bildirilir. Bu, aynı işlevi birden çok kez bildirmeye yol açabilir. Bunun derlenmesi daha zordur ve hatta hatalara bile yol açabilir.

Doğru kullanım örneği:

Dosya1: "Clazz.h"

//This file sets up the class with a prototype body. 

class Clazz
{
public:
    void Fun1();//This is a Fun1 Prototype. 
};

Dosya2: "Clazz.cpp"

#include "Clazz.h" 
//this file gives Fun1() (prototyped in the header) a body once.

void Clazz::Fun1()
{
    //Do stuff...
}

Dosya3: "UseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.

File4: "alsoUseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header. 

File5: "DoNotUseClazzHeader.cpp"

//here we do not include Clazz.h. So this is another scope. 
class Clazz
{
public:
    void Fun1()
    {
         //Do something else...
    }
};

class MyClazz; //this is a totally different thing. 
MyClazz.Fun1(); //this does something else. 

Yani Clazz MyClazzve Clazz MyClazz2?
Chupo_cro

4

Üye işlevleri, sınıf tanımı içinde veya kapsam çözümleme operatörü, ::. Kullanılarak ayrı ayrı tanımlanabilir. Sınıf tanımında bir üye işlevi tanımlamak, satır içi belirticiyi kullanmasanız bile işlevi satır içi olarak bildirir. Dolayısıyla, Volume () işlevini aşağıdaki gibi tanımlayabilirsiniz:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

     double getVolume(void)
     {
        return length * breadth * height;
     }
};

Dilerseniz kapsam çözümleme operatörünü kullanarak sınıf dışında da aynı işlevi tanımlayabilirsiniz, :: aşağıdaki gibi

double Box::getVolume(void)
{
   return length * breadth * height;
}

Burada önemli olan tek nokta, sınıf adını :: operatöründen hemen önce kullanmanız gerektiğidir. Bir üye işlevi, yalnızca o nesneyle ilgili verileri aşağıdaki gibi işleyeceği bir nesne üzerinde bir nokta operatörü (.) Kullanılarak çağrılacaktır:

Box myBox;           

myBox.getVolume();  

( http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), her iki yol da yasaldır.

Ben bir uzman değilim, ama sanırım bir dosyaya sadece bir sınıf tanımı koyarsanız, o zaman gerçekten önemli değil.

ancak iç sınıf gibi bir şey uygularsanız veya birden fazla sınıf tanımınız varsa, ikincisini okumak ve sürdürmek zor olacaktır.


1
Bu bağlantıdan ilgili içeriği gönderinizin gövdesine getirebilir ve böylece ölü bağlantılara karşı geleceğe hazır olabilir misiniz? Teşekkürler
JustinJDavies

2

İlki, başlık dosyasına (sınıfın bildiriminin bulunduğu yer) konulmalıdır. İkincisi, herhangi bir yerde, başlık veya genellikle bir kaynak dosyası olabilir. Uygulamada, sınıf bildirimine küçük işlevler koyabilirsiniz (bu, onları dolaylı olarak satır içi olarak bildirir, ancak sonuçta bunların satır içi olup olmayacağına derleyici karar verir). Bununla birlikte, çoğu işlevin başlıkta bir bildirimi ve ikinci örneğinizde olduğu gibi bir cpp dosyasında uygulaması vardır. Ve hayır, bunun neden daha az okunabilir olması için herhangi bir neden göremiyorum. Bir tür için uygulamayı birkaç cpp dosyasına bölebileceğinizden bahsetmiyorum bile.


1

Bir sınıf içinde tanımlanan bir işlev, varsayılan olarak bir satır içi işlev olarak kabul edilir. İşlevinizi dışarıda tanımlamanız için basit bir neden:

Sınıfın bir kurucusu, sanal işlevleri denetler ve uygun VTABLE'a veya sanal yöntem tablosuna işaret etmek için bir sanal işaretçiyi başlatır , temel sınıf yapıcısını çağırır ve geçerli sınıfın değişkenlerini başlatır, böylece aslında bazı işler yapar.

Satır içi işlevler, işlevler çok karmaşık olmadığında ve işlev çağrısının ek yükünden kaçındığında kullanılır. (Ek yük, donanım düzeyinde bir atlama ve dallanma içerir.) Ve yukarıda açıklandığı gibi, kurucunun satır içi olarak kabul edilmesi kadar basit değildir.


"satır içi" ifadesinin satır içi ile neredeyse hiçbir ilgisi yoktur. Satır içi tanımlanan üye işlevlerinin dolaylı olarak satır içi olarak bildirilmesi, ODR ihlallerini önlemek içindir.
Big Temp

0

Satır içi işlevler (bunları sınıfta bildirdiğiniz işlevler) her çağırışınızda ana bellek kodunuza yapıştırılır. Fonksiyonu sınıfın dışında ilan ettiğinizde, fuction çağırdığınızda aynı bellekten gelir. Bu yüzden çok daha iyi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.