Tách mã lớp thành tệp tiêu đề và cpp


169

Tôi bối rối về cách tách mã khai báo và mã khai báo của một lớp đơn giản thành một tệp tiêu đề và cpp mới. Ví dụ, làm thế nào tôi có thể tách mã cho lớp sau?

class A2DD
{
  private:
  int gx;
  int gy;

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

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

12
Chỉ cần một vài ý kiến: Nhà xây dựng nên luôn luôn sử dụng danh sách khởi tạo thay vì đặt các thành viên trong cơ thể. Để có một lời giải thích tốt và đơn giản, hãy xem: codeguru.com/forum/showthread.php?t=464084 Ngoài ra, ít nhất là ở hầu hết các địa điểm, thường có trường công khai ở trên cùng. Nó sẽ không ảnh hưởng đến bất cứ điều gì, nhưng vì các lĩnh vực công cộng là tài liệu của lớp học của bạn, nên có ý nghĩa khi đặt nó ở trên cùng.
martiert

2
@martiert Có public:thành viên ở phía trên có thể ảnh hưởng đến một nhiều , nếu người dùng di chuyển chúng theo lời khuyên này - nhưng đã đặt hàng phụ thuộc giữa các thành viên và vẫn chưa nhận thức được rằng các thành viên được khởi tạo theo thứ tự của việc kê khai ;-)
underscore_d

1
@underscore_d đó là sự thật. Nhưng một lần nữa, tất cả chúng ta đều biên dịch với các cảnh báo là lỗi và tất cả các cảnh báo chúng ta có thể nghĩ ra, phải không? Điều đó ít nhất sẽ cho bạn biết rằng bạn đang làm hỏng việc này, nhưng vâng, mọi người sử dụng cách để cảnh báo nhỏ, và bỏ qua chúng :(
martiert

@martiert Điểm hay, hơi quên rằng tạo ra các cảnh báo - nếu hầu hết các cảnh báo chỉ được đọc :-) Tôi sử dụng chúng và cố gắng mã hóa tất cả chúng. Một vài điều không thể tránh khỏi - vì vậy tôi nói 'cảm ơn vì đã cảnh báo, nhưng tôi biết tôi đang làm gì!' - nhưng hầu hết được cố định tốt nhất để tránh nhầm lẫn sau này.
gạch dưới

Có các lĩnh vực công cộng ở đầu chỉ là một phong cách, mà quá nhiều người đã không may chấp nhận theo ý kiến ​​của tôi. Ngoài ra, bạn cần ghi nhớ một số điều như @martiert đã đề cập.
Vassilis

Câu trả lời:


232

Khai báo lớp đi vào tệp tiêu đề. Điều quan trọng là bạn thêm các bộ #ifndefbảo vệ bao gồm hoặc nếu bạn đang ở trên nền tảng MS, bạn cũng có thể sử dụng #pragma once. Ngoài ra tôi đã bỏ qua phần riêng tư, theo mặc định các thành viên lớp C ++ là riêng tư.

// A2DD.h
#ifndef A2DD_H
#define A2DD_H

class A2DD
{
  int gx;
  int gy;

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

};

#endif

và việc thực hiện diễn ra trong tệp CPP:

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

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

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

52
Hãy nhớ rằng nếu bạn đang thực hiện lập trình mẫu, thì bạn phải giữ mọi thứ trong tệp .h để trình biên dịch sẽ khởi tạo đúng mã tại thời điểm biên dịch.
linello

2
Bạn có những #ifndefthứ trong tiêu đề?
Ferenc Deak

4
Vì vậy, điều này có nghĩa là tất cả các tệp bao gồm tệp tiêu đề của bạn sẽ "nhìn thấy" các thành viên riêng tư. Nếu ví dụ bạn muốn xuất bản một lib và tiêu đề của nó, bạn phải hiển thị các thành viên riêng của lớp?
Gauthier

1
Không, có thành ngữ triển khai riêng tư tuyệt vời: en.wikipedia.org/wiki/Opaque_pulum Bạn có thể sử dụng nó để ẩn các chi tiết thực hiện.
Ferenc Deak

3
Tiểu nitlog với từ ngữ: "Khai báo lớp đi vào tệp tiêu đề". Đây thực sự là một tuyên bố, nhưng nó cũng là một định nghĩa, nhưng vì sau này bao gồm cả định nghĩa trước đây tôi muốn nói rằng định nghĩa lớp đi vào tệp tiêu đề. Trong đơn vị dịch thuật, bạn có định nghĩa về các hàm thành viên, không phải định nghĩa của lớp. Tôi đồng ý, điều này có thể đáng giá một chỉnh sửa nhỏ?
Lubgr

17

Nói chung .h của bạn chứa phân lớp, đó là tất cả dữ liệu của bạn và tất cả các khai báo phương thức của bạn. Như thế này trong trường hợp của bạn:

A2DD.h:

class A2DD
{
  private:
  int gx;
  int gy;

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

Và sau đó .cpp của bạn chứa các triển khai của các phương thức như thế này:

A2DD.cpp:

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

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

7

Điều quan trọng là chỉ ra cho độc giả vấp phải câu hỏi này khi nghiên cứu chủ đề theo cách rộng hơn rằng thủ tục câu trả lời được chấp nhận là không bắt buộc trong trường hợp bạn chỉ muốn chia dự án của mình thành các tệp. Nó chỉ cần thiết khi bạn cần nhiều triển khai của các lớp đơn. Nếu triển khai của bạn cho mỗi lớp là một, chỉ cần một tệp tiêu đề cho mỗi lớp là đủ.

Do đó, từ ví dụ của câu trả lời được chấp nhận, chỉ có phần này là cần thiết:

#ifndef MYHEADER_H
#define MYHEADER_H

//Class goes here, full declaration AND implementation

#endif

Các định nghĩa tiền xử lý #ifndef, vv cho phép nó được sử dụng nhiều lần.

Tái bút Chủ đề trở nên rõ ràng hơn khi bạn nhận ra C / C ++ là 'ngu ngốc' và #include chỉ là một cách để nói "kết xuất văn bản này tại điểm này".


bạn có thể làm điều này bằng cách đặt các tệp "split" vào .cpp, hoặc chỉ .hthực sự "tốt" cho phương pháp tổ chức mã này?
Benny Jobigan

1
Tôi nghĩ rằng một số dự án phân chia các tệp tiêu đề và (một) tệp để chúng có thể phân phối các tệp tiêu đề dễ dàng mà không tiết lộ mã nguồn của các triển khai.
Carl G

Tôi rất vui vì bạn đã chỉ ra điều này bởi vì ban đầu tôi học về C ++ sau đó đã chuyển sang C # nhiều năm trước và gần đây đã làm lại rất nhiều C ++ và tôi quên mất việc chia nhỏ các tập tin trở nên tẻ nhạt và khó chịu như thế nào và chỉ bắt đầu đưa mọi thứ vào các tiêu đề. Tôi đã tìm kiếm xung quanh để tìm kiếm bất cứ ai đưa ra lý do chính đáng KHÔNG làm điều đó khi tôi tìm thấy điều này. @CarlG có một điểm tốt, nhưng khác với kịch bản đó tôi nghĩ làm tất cả nội tuyến là con đường để đi.
Peter Moore

6

Về cơ bản một cú pháp sửa đổi của khai báo / định nghĩa hàm:

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

A2DĐ.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;
  }

Ý tưởng là giữ tất cả chữ ký chức năng và các thành viên trong tệp tiêu đề.
Điều này sẽ cho phép các tệp dự án khác để xem lớp trông như thế nào mà không cần phải biết việc thực hiện.

Và bên cạnh đó, bạn có thể bao gồm các tệp tiêu đề khác trong triển khai thay vì tiêu đề. Điều này rất quan trọng vì bất kỳ tiêu đề nào được bao gồm trong tệp tiêu đề của bạn sẽ được đưa vào (kế thừa) trong bất kỳ tệp nào khác có chứa tệp tiêu đề của bạn.


4

Bạn để lại các khai báo trong tệp tiêu đề:

class A2DD
{
  private:
  int gx;
  int gy;

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

Và đặt các định nghĩa trong tập tin thực hiện.

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

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

Bạn có thể kết hợp cả hai ( getSum()ví dụ để lại định nghĩa trong tiêu đề). Điều này rất hữu ích vì nó cung cấp cho trình biên dịch một cơ hội tốt hơn để nội tuyến chẳng hạn. Nhưng điều đó cũng có nghĩa là việc thay đổi triển khai (nếu còn lại trong tiêu đề) có thể kích hoạt việc xây dựng lại tất cả các tệp khác có chứa tiêu đề.

Lưu ý rằng đối với các mẫu, bạn cần giữ tất cả trong tiêu đề.


1
Đưa các thành viên và chức năng riêng tư vào tệp tiêu đề không được coi là rò rỉ chi tiết triển khai?
Jason

1
@Jason, loại. Đó là những chi tiết thực hiện cần thiết . Ví dụ, tôi phải biết một lớp sẽ tiêu thụ bao nhiêu không gian trên ngăn xếp. Chức năng thực hiện là không cần thiết cho các đơn vị biên dịch khác.
Paul Draper

1

Thông thường, bạn chỉ đặt các khai báo và các hàm nội tuyến thực sự ngắn trong tệp tiêu đề:

Ví dụ:

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

Tôi sẽ không tham khảo ví dụ của bạn vì nó khá đơn giản cho một câu trả lời chung chung (ví dụ: nó không chứa các hàm templated, buộc bạn phải thực hiện chúng trên tiêu đề), điều tôi làm theo quy tắc ngón tay cái là pimpl cách diễn đạt

Nó có khá nhiều lợi ích khi bạn có được thời gian biên dịch nhanh hơn và đường cú pháp:

class->member thay vì class.member

Hạn chế duy nhất là con trỏ phụ bạn phải trả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.