Deklarasi fungsi di dalam atau di luar kelas


91

Saya seorang pengembang JAVA yang mencoba mempelajari C ++, tetapi saya tidak benar-benar tahu apa praktik terbaik untuk deklarasi fungsi standar.

Di dalam kelas:

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

Atau di luar:

class Clazz
{
public:
    void Fun1();
}

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

Saya merasa bahwa yang kedua kurang bisa dibaca ...


1
Sebenarnya ada 3 opsi di sini. Contoh kedua Anda dapat memiliki definisi fungsi di file header (tapi masih tidak sebaris), atau di .cppfile terpisah .
Cody Gray

Pertanyaan ini mungkin membantu Anda memahami.
Björn Pollex

3
Sekadar catatan: deklarasi selalu di dalam kelas, tetapi definisi ada di dalam atau di luar. Judul dan isi pertanyaan harus tunduk pada s / declaration / definition / Don't believe me? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev

1
Definisi fungsi di dalam kelas harus dihindari. Mereka dianggap secara implisit inline.
John Strood

@Johnny begitu? inlinehanya melonggarkan aturan satu definisi, yang diperlukan jika unit terjemahan lain menggunakanClazz
Caleth

Jawaban:


57

C ++ berorientasi objek, dalam arti mendukung paradigma berorientasi objek untuk pengembangan perangkat lunak.

Namun, berbeda dari Java, C ++ tidak memaksa Anda untuk mengelompokkan definisi fungsi di kelas: cara standar C ++ untuk mendeklarasikan fungsi adalah dengan mendeklarasikan fungsi, tanpa kelas apa pun.

Jika sebaliknya Anda berbicara tentang deklarasi / definisi metode maka cara standar adalah dengan meletakkan hanya deklarasi dalam file include (biasanya dinamai .hatau .hpp) dan definisi dalam file implementasi terpisah (biasanya dinamai .cppatau.cxx ). Saya setuju ini memang agak menjengkelkan dan membutuhkan beberapa duplikasi tetapi begitulah cara bahasanya dirancang.

Untuk eksperimen cepat dan proyek file tunggal, semuanya akan berhasil ... tetapi untuk proyek yang lebih besar, pemisahan ini secara praktis diperlukan.

Catatan: Meskipun Anda tahu Java, C ++ adalah bahasa yang sama sekali berbeda ... dan ini adalah bahasa yang tidak bisa dipelajari dengan bereksperimen. Alasannya adalah karena ini adalah bahasa yang agak rumit dengan banyak asimetri dan pilihan yang tampaknya tidak logis, dan yang terpenting, ketika Anda membuat kesalahan, tidak ada "malaikat kesalahan runtime" untuk menyelamatkan Anda seperti di Java ... tetapi ada " daemon perilaku tidak terdefinisi ".

Satu-satunya cara yang masuk akal untuk mempelajari C ++ adalah dengan membaca ... tidak peduli seberapa pintar Anda tidak mungkin Anda dapat menebak apa yang diputuskan panitia (sebenarnya menjadi pintar terkadang malah menjadi masalah karena jawaban yang benar tidak logis dan konsekuensi dari sejarah warisan.)

Pilih saja satu atau dua buku yang bagus dan bacalah dari sampul ke sampul.


7
Jika seseorang berasal dari Java dan meminta bantuan tentang C ++, lalu apa yang dikatakannya jika Anda mengatakan "bahasa yang Anda pahami terobsesi dengan sesuatu"? Dia tidak memiliki perbandingan dengan bahasa lain, jadi ini tidak memberi tahu apa-apa. Lebih baik daripada menggunakan kata yang sangat berkonotasi emosional seperti terobsesi, yang tidak memberi tahu OP banyak, Anda mungkin mempertimbangkan untuk tidak memasukkan bagian ini. Selain itu, apa konteks dari "menggunakan kelas untuk setiap"? Di Java, Anda tidak menggunakan kelas untuk suatu metode. Anda tidak menggunakan kelas untuk variabel. Anda tidak menggunakan kelas untuk file..Jadi apa "segalanya" di sini? Mengoceh?
Daniel S.

3
@DanielS: Hapus bagian itu karena tampaknya menyinggung Anda (tidak tahu mengapa). Yang pasti saya tidak mengomel tentang Java karena saya tidak benar-benar menggunakan Java sama sekali, saya hanya berpikir pada saat itu bahwa OOP sebagai Pemrograman Terobsesi Objek adalah lelucon yang lucu, padahal ternyata tidak. Saya telah menjadi programmer bersertifikat Java 1.1 tetapi saat itu memutuskan bahwa, kecuali dipaksa karena alasan tertentu, saya tidak akan menggunakan "bahasa pemrograman" itu dan sejauh ini saya berhasil menghindarinya.
6502

Terima kasih, saya pikir sudah jauh lebih baik sekarang. Maaf jika saya terdengar tersinggung. Saya akan mencoba menjadi lebih positif lain kali.
Daniel S.

15
Tidak menjawab pertanyaan
Petr Peller

1
@PetrPeller: apa bagian dari paragraf ketiga yang tidak jelas bagi Anda?
6502

27

Yang pertama mendefinisikan fungsi anggota Anda sebagai fungsi sebaris , sedangkan yang kedua tidak. Definisi fungsi dalam hal ini berada di header itu sendiri.

Implementasi kedua akan menempatkan definisi fungsi di file cpp.

Keduanya berbeda secara semantik dan ini bukan hanya soal gaya.


2
cplusplus.com/doc/tutorial/classes memberikan jawaban yang sama: "Satu-satunya perbedaan antara mendefinisikan fungsi anggota kelas secara lengkap di dalam kelasnya atau hanya menyertakan prototipe dan kemudian definisinya, adalah bahwa dalam kasus pertama fungsi tersebut secara otomatis akan menjadi dianggap sebagai fungsi anggota sebaris oleh kompilator, sedangkan di urutan kedua akan menjadi fungsi anggota kelas normal (bukan sebaris), yang sebenarnya mengandaikan tidak ada perbedaan dalam perilaku. "
Buttons840

18

Definisi fungsi lebih baik di luar kelas. Dengan begitu, kode Anda dapat tetap aman jika diperlukan. File header seharusnya hanya memberikan deklarasi.

Misalkan seseorang ingin menggunakan kode Anda, Anda dapat memberinya file .h dan file .obj (diperoleh setelah kompilasi) dari kelas Anda. Dia tidak membutuhkan file .cpp untuk menggunakan kode Anda.

Dengan begitu, implementasi Anda tidak terlihat oleh orang lain.


10

Metode "Di dalam kelas" (I) melakukan hal yang sama seperti metode "di luar kelas" (O).

Namun, (I) dapat digunakan ketika kelas hanya digunakan dalam satu file (di dalam file .cpp). (O) digunakan saat berada di file header. file cpp selalu dikompilasi. File header dikompilasi saat Anda menggunakan #include "header.h".

Jika Anda menggunakan (I) di file header, fungsi (Fun1) akan dideklarasikan setiap kali Anda menyertakan #include "header.h". Ini dapat menyebabkan mendeklarasikan fungsi yang sama beberapa kali. Ini lebih sulit untuk dikompilasi, dan bahkan dapat menyebabkan kesalahan.

Contoh penggunaan yang benar:

File1: "Clazz.h"

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

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

File2: "Clazz.cpp"

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

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

File3: "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: "JugaUseClazz.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. 

Maksudmu Clazz MyClazzdan Clazz MyClazz2?
Chupo_cro

4

Fungsi anggota dapat didefinisikan dalam definisi kelas atau secara terpisah menggunakan operator resolusi lingkup, ::. Mendefinisikan fungsi anggota dalam definisi kelas mendeklarasikan fungsi sebaris, meskipun Anda tidak menggunakan penentu sebaris. Jadi, Anda dapat menentukan fungsi Volume () seperti di bawah ini:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

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

Jika Anda suka, Anda dapat mendefinisikan fungsi yang sama di luar kelas menggunakan operator resolusi lingkup, :: sebagai berikut

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

Di sini, satu-satunya poin penting adalah Anda harus menggunakan nama kelas sebelum :: operator. Fungsi anggota akan dipanggil menggunakan operator titik (.) Pada objek yang akan memanipulasi data yang terkait dengan objek itu hanya sebagai berikut:

Box myBox;           

myBox.getVolume();  

(dari: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), kedua cara tersebut legal.

Saya bukan ahli, tapi menurut saya, jika Anda hanya memasukkan satu definisi kelas dalam satu file, maka itu tidak terlalu penting.

tetapi jika Anda menerapkan sesuatu seperti kelas dalam, atau Anda memiliki definisi kelas ganda, definisi kelas kedua akan sulit dibaca dan dipertahankan.


1
Dapatkah Anda membawa konten yang relevan dari tautan itu ke dalam badan kiriman Anda, dan dengan demikian membuktikan masa depan terhadap tautan mati? Terima kasih
JustinJDavies

2

Yang pertama harus diletakkan di file header (tempat deklarasi kelas berada). Yang kedua bisa di mana saja, baik header atau, biasanya, file sumber. Dalam praktiknya, Anda bisa meletakkan fungsi kecil di deklarasi kelas (yang mendeklarasikannya secara implisit, meskipun compiler-lah yang pada akhirnya memutuskan apakah mereka akan sebaris atau tidak). Namun, sebagian besar fungsi memiliki deklarasi di header dan implementasinya dalam file cpp, seperti dalam contoh kedua Anda. Dan tidak, saya tidak melihat alasan mengapa ini menjadi kurang terbaca. Belum lagi Anda benar-benar dapat membagi implementasi untuk suatu tipe di beberapa file cpp.


1

Fungsi yang didefinisikan di dalam kelas secara default diperlakukan sebagai fungsi sebaris. Alasan sederhana mengapa Anda harus mendefinisikan fungsi Anda di luar:

Konstruktor kelas memeriksa fungsi virtual dan menginisialisasi penunjuk virtual untuk menunjuk ke VTABLE yang tepat atau tabel metode virtual , memanggil konstruktor kelas dasar, dan menginisialisasi variabel kelas saat ini, sehingga sebenarnya melakukan beberapa pekerjaan.

Fungsi inline digunakan ketika fungsi tidak begitu rumit dan menghindari overhead pemanggilan fungsi. (Overhead mencakup lompatan dan cabang pada tingkat perangkat keras.) Dan seperti dijelaskan di atas, konstruktor tidak sesederhana untuk dianggap sebaris.


"inline" secara praktis tidak ada hubungannya dengan inline. Fakta bahwa fungsi anggota yang didefinisikan sebaris secara implisit dideklarasikan sebaris ada untuk menghindari pelanggaran ODR.
Big Temp

0

Fungsi sebaris (fungsi saat Anda mendeklarasikannya di kelas) setiap kali memanggilnya, fungsi tersebut ditempelkan dalam kode memeory utama Anda. Sedangkan saat Anda mendeklarasikan fungsi di luar kelas, saat Anda memanggil fuction itu berasal dari memori yang sama. Itulah mengapa jauh lebih baik.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.