Memisahkan kode kelas menjadi file header dan cpp


170

Saya bingung bagaimana memisahkan implementasi dan deklarasi kode dari kelas sederhana menjadi file header dan cpp baru. Sebagai contoh, bagaimana saya memisahkan kode untuk kelas berikut?

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int getSum()
  {
    return gx + gy;
  }
};

12
Hanya beberapa komentar: Konstruktor harus selalu menggunakan daftar inisialisasi daripada mengatur anggota dalam tubuh. Untuk penjelasan yang baik dan sederhana, lihat: codeguru.com/forum/showthread.php?t=464084 Juga, setidaknya di sebagian besar tempat, kebiasaan memiliki bidang publik di atas. Ini tidak akan memengaruhi apa pun, tetapi karena bidang publik adalah dokumentasi kelas Anda, masuk akal untuk melakukannya di atas.
martiert

2
@martiert Memiliki public:anggota di atas dapat banyak mempengaruhi , jika pengguna memindahkan mereka sesuai dengan saran ini - tetapi telah memesan ketergantungan antar anggota dan belum menyadari bahwa anggota diinisialisasi dalam urutan deklarasi mereka ;-)
underscore_d

1
@underscore_d itu benar. Tapi sekali lagi, kita semua mengkompilasi dengan peringatan sebagai kesalahan dan semua peringatan yang bisa kita pikirkan, kan? Setidaknya itu akan memberi tahu Anda bahwa Anda mengacaukannya, tapi ya, orang menggunakan sedikit peringatan, dan abaikan saja :(
martiert

@martiert Poin bagus, agak lupa yang menghasilkan peringatan - jika hanya peringatan yang dibaca oleh sebagian besar :-) Saya menggunakannya dan mencoba untuk kode mereka semua. Beberapa tidak dapat dihindari - jadi saya mengatakan 'terima kasih atas peringatannya, tetapi saya tahu apa yang saya lakukan!' - tetapi sebagian besar yang terbaik diperbaiki untuk menghindari kebingungan nanti.
underscore_d

Memiliki bidang publik di atas hanyalah gaya, yang sayangnya terlalu banyak diadopsi menurut saya. Selain itu, Anda perlu mengingat beberapa hal seperti yang disebutkan @martiert.
Vassilis

Jawaban:


233

Deklarasi kelas masuk ke file header. Penting bahwa Anda menambahkan #ifndefpenjaga sertakan, atau jika Anda menggunakan platform MS, Anda juga dapat menggunakannya #pragma once. Saya juga telah menghapus privat, secara default anggota kelas C ++ bersifat privat.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

public:
  A2DD(int x,int y);
  int getSum();

};

#endif

dan implementasinya ada di file CPP:

// A2DD.cpp
#include "A2DD.h"

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

53
Ingatlah bahwa jika Anda melakukan pemrograman template, maka Anda harus menyimpan semuanya dalam file .h sehingga kompiler akan membuat kode yang benar pada saat kompilasi.
linello

2
apakah Anda memiliki #ifndefbarang di header?
Ferenc Deak

4
Jadi ini berarti bahwa semua file yang menyertakan file header Anda akan "melihat" anggota pribadi. Jika misalnya Anda ingin menerbitkan lib dan tajuknya, Anda harus menunjukkan kepada anggota pribadi kelas tersebut?
Gauthier

1
Tidak, ada idiom implementasi privat yang luar biasa: en.wikipedia.org/wiki/Opaque_pointer Anda dapat menggunakannya untuk menyembunyikan detail implementasi.
Ferenc Deak

3
Minor nitpick dengan kata-kata: "Deklarasi kelas masuk ke file header". Ini memang sebuah deklarasi, tetapi juga sebuah definisi, tetapi karena yang terakhir menyertakan yang pertama saya lebih suka mengatakan bahwa definisi kelas masuk ke file header. Di unit terjemahan, Anda memiliki definisi fungsi anggota, bukan definisi kelas. Saya setuju, ini mungkin layak diedit kecil?
lubgr

17

Secara umum .h Anda berisi definisi kelas, yang merupakan semua data Anda dan semua deklarasi metode Anda. Seperti ini dalam kasus Anda:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);    
  int getSum();
};

Dan .cpp Anda berisi implementasi metode seperti ini:

A2DD.cpp:

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

7

Penting untuk menunjukkan kepada pembaca yang tersandung pada pertanyaan ini ketika meneliti subjek dengan cara yang lebih luas bahwa prosedur jawaban yang diterima tidak diperlukan jika Anda hanya ingin membagi proyek Anda menjadi file. Ini hanya diperlukan ketika Anda membutuhkan beberapa implementasi dari satu kelas. Jika implementasi Anda per kelas adalah satu, cukup satu file header untuk masing-masing sudah cukup.

Karenanya, dari contoh jawaban yang diterima hanya bagian ini yang diperlukan:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Definisi preprocessor #ifndef dll. Memungkinkan untuk digunakan beberapa kali.

PS. Topiknya menjadi lebih jelas setelah Anda menyadari C / C ++ 'bodoh' dan #include hanyalah cara untuk mengatakan "buang teks ini di tempat ini".


dapatkah Anda melakukan ini dengan meletakkan "split" file .cpp, atau hanya .hbenar-benar "baik" untuk metode kode organisasi ini?
Benny Jobigan

1
Saya berpikir bahwa beberapa proyek membagi header dan file implementasi (tunggal) sehingga mereka dapat mendistribusikan file header dengan mudah tanpa mengungkapkan kode sumber implementasi.
Carl G

Saya sangat senang Anda menunjukkan ini karena saya awalnya belajar di C ++ kemudian beralih ke C # bertahun-tahun yang lalu dan baru-baru ini telah melakukan banyak C ++ lagi dan saya lupa betapa membosankan dan menjengkelkannya memecah file-file itu dan baru mulai memasukkan semuanya ke dalam header. Saya mencari-cari orang yang memberikan alasan yang bagus untuk TIDAK melakukan itu ketika saya menemukan ini. @CarlG punya poin bagus, tapi selain skenario itu, saya pikir melakukan itu semua adalah jalan yang harus ditempuh.
Peter Moore

6

Pada dasarnya sintaks modifikasi deklarasi fungsi / definisi:

a2dd.h

class A2DD
{
private:
  int gx;
  int gy;

public:
  A2DD(int x,int y);

  int getSum();
};

a2dd.cpp

A2DD::A2DD(int x,int y)
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

5

A2DD.h

class A2DD
{
  private:
  int gx;
  int gy;

  public:
  A2DD(int x,int y);

  int getSum();
};

A2DD.cpp

  A2DD::A2DD(int x,int y)
  {
    gx = x;
    gy = y;
  }

  int A2DD::getSum()
  {
    return gx + gy;
  }

Idenya adalah untuk menyimpan semua tanda tangan fungsi dan anggota dalam file header.
Ini akan memungkinkan file proyek lainnya untuk melihat bagaimana kelas terlihat tanpa harus mengetahui implementasinya.

Dan di samping itu, Anda kemudian dapat memasukkan file header lainnya dalam implementasi bukan header. Ini penting karena header mana pun yang termasuk dalam file header Anda akan dimasukkan (diwariskan) dalam file lain yang menyertakan file header Anda.


4

Anda meninggalkan deklarasi di file header:

class A2DD
{
  private:
  int gx;
  int gy;

  public:
    A2DD(int x,int y); // leave the declarations here
    int getSum();
};

Dan letakkan definisi dalam file implementasi.

A2DD::A2DD(int x,int y) // prefix the definitions with the class name
{
  gx = x;
  gy = y;
}

int A2DD::getSum()
{
  return gx + gy;
}

Anda dapat menggabungkan keduanya (biarkan getSum()definisi di header misalnya). Ini berguna karena memberi kompiler kesempatan yang lebih baik untuk inlining misalnya. Tetapi itu juga berarti bahwa mengubah implementasi (jika dibiarkan di header) dapat memicu pembangunan kembali semua file lain yang termasuk header.

Perhatikan bahwa untuk template, Anda harus menyimpan semuanya di header.


1
Menempatkan anggota dan fungsi pribadi dalam file header tidak dianggap membocorkan detail implementasi?
Jason

1
@ Jason, semacam. Itu adalah detail implementasi yang diperlukan . Misalnya, saya harus tahu berapa banyak ruang yang akan dikonsumsi kelas pada stack. Implementasi fungsi tidak diperlukan untuk unit kompilasi lainnya.
Paul Draper

1

Biasanya Anda hanya menempatkan deklarasi dan fungsi inline yang sangat singkat di file header:

Misalnya:

class A {
 public:
  A(); // only declaration in the .h unless only a short initialization list is used.

  inline int GetA() const {
    return a_;
  }

  void DoSomethingCoplex(); // only declaration
  private:
   int a_;
 };

0

Saya tidak akan merujuk contoh Anda terlalu sederhana untuk jawaban umum (misalnya tidak mengandung fungsi templated, yang memaksa Anda untuk mengimplementasikannya di header), apa yang saya ikuti sebagai aturan praktis adalah pimpl idiom

Ini memiliki beberapa manfaat karena Anda mendapatkan waktu kompilasi lebih cepat dan gula sintaksis:

class->member dari pada class.member

Satu-satunya kelemahan adalah pointer ekstra yang Anda bayar.

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.