Cách tổ chức file C và C++ (phần 3)

Khắc phục trường hợp thứ nhất:

May mắn thay, trường hợp này khá đơn giản, và dễ tránh nếu bạn hiểu nó.

Lỗi thứ nhất, khi file nguồn không biên dịch bởi vì một trong số những định nghĩa chưa được khai báo, dễ dàng để sửa. Đơn giản là #include file chứa định nghĩa cần thiết. Nếu file header của bạn được tổ chức hợp lý và đặt tên tốt, điều này sẽ rất dễ dàng. Nếu bạn cần sử dụng cấu trúc Sprite, thì bạn chi cần thêm vào #include “Sprite.h” vào bất cứ file nào có nó. 1 lỗi mà nhiều lập trình viên thường mắc phải là cho rằng 1 file đã được #include đơn giản vì nó đã được #include trong 1 file khác.

Example:
/* Header1.h */
#include “header2.h”
class ClassOne { … };

.

/* Header2.h */
class ClassTwo { … };

.

/* File1.cpp */
#include “Header1.h”
ClassOne myClassOne_instance;
ClassTwo myClassTwo_instance;

Trong trường hợp này, File1.cpp sẽ biên dịch tốt, vì đã include Header1.h và include gián tiếp Header2.h, có nghĩa là File1.cpp có thể truy xuất đến lớp Class2. Nhưng chuyện gì sẽ xảy ra nếu một thời gian sau, ai đó cho rằng Header1.h không cần thiết phải #include Header2.h? Họ có thể xoá dòng #include đó, và đột nhiên File1.cpp sẽ không thể biên dịch được.

Chìa khóa ở đây là, cần phải dứt khoát khi #include bất kì header nào bạn cần cho file nguồn để biên dịch. Bạn không nên dựa vào những file header include gián tiếp các header bổ sung, mà có thể sẽ thay đổi. Những #include bổ sung cũng xử lý như tài liệu, mà các mã trong file phải phụ thuộc vào. Do đó đừng xoá chúng nếu bạn biết bạn cần header đó cần phải include đâu đó.

Khắc phục trường hợp thứ 2:

Sự phụ trường vòng tròn là vấn đề thường gặp trong kĩ thuật phần mềm. Nhiều cấu trúc liên kết lẫn nhau, và tất cả đều phải biết lẫn nhau. Thông thường nó có dạng như thế này:

/* Parent.h */
#include “child.h”
class Parent
{
Child* theChild;
};

.

/* Child.h */
#include “parent.h”
class Child
{
Parent* theParent;
};

Trường hợp này thật sự đơn giản. Cấu trúc Parent thật ra không cần phải biết chi tiết lớp Child, khi nó chỉ chứa 1 con trỏ của lớp Child. Con trỏ không cần biết nó chỉ đến đâu, do đó bạn không cần phải định nghĩa cấn trúc hoặc lớp để chứa con trỏ. Do đó dòng #include ở đây là không cần thiết. Tuy nhiên, lỗi “undeclared identifier” sẽ xuất hiện khi nó gặp từ ‘Child’, nên bạn cần cho trình biên dịch biết rằng Child là 1 lớp mà bạn cần chỉ đến. Sử dụng khai báo trước, đưa ra dạng hoặc định nghĩa lớp mà không có chi tiết bên trong. Ví dụ:

/* Parent.h */
class Child; /* Forward declaration of Child; */
class Parent
{
Child* theChild;
};

Chú ý rằng dòng #include được thay thế bằng một khai báo trước. Điều này cho phép thoát khỏi sự phụ thuộc vòng tròn giữa Parent.h và Child.h. Hơn nữa, tốc độ biên dịch sẽ tăng lên khi bạn có ít file include hơn. Trong trường hợp này, file Child.h cũng phải khai báo trước “class Parent;” Chỉ khi bạn chỉ tham chiếu đến con trỏ mà không phải dạng thật sự, bạn không cần phải #include toàn bộ định nghĩa. Trong 99% trường hợp, điều này có thể áp dụng cho cả 2 phía của vòng tròn và không cần #include 1 header trong file header kia, để loại trừ sự phụ thuộc vòng tròn.

Dĩ nhiên, trong những file parent.c và child.c, bạn cần phải #include cả parent.h và child.h.

Một trường hợp khác làm xuất hiện sự phụ thuộc vòng tròn là những hàm inline định nghĩa bên trong header (để tăng tốc độ). Để cho hàm có thể thực thi, nó cần phải biết chi tiết của lớp. Do đó, file header có chứa hàm inline cần phải #include khi những hàm đó cần được biên dịch. Lời khuyên đầu tiên là bạn chỉ nên tạo 1 hàm inline khi nó thật sự cần thiết. Khi bạn kiểm tra và chắc chắn những hàm cần phải ở dạng inline (và dĩ nhiên là trong file header), cố gắng và đảm bảo rằng trong 2 file header phụ thuộc lẫn nhau chỉ có 1 file là được chứa những hàm inline.

Chú ý rằng bạn việc loại trừ sự phụ thuộc không phải lúc nào cũng thực hiện được. Nhiều cấu trúc và lớp bao gồm nhiều cấu trúc và lớp khác, mà sự phụ thuộc là không thể tránh khỏi. Tuy nhiên, chỉ khi sự phụ thuộc là 1 chiều, nó sẽ được sửa chữa khi biên dịch, và sẽ không có vấn đề gì xảy ra.

Có nhiều phương pháp phức tạp hơn để xử lý sự phụ thuộc vòng tròn, nhưng nó không liên quan đến mục đích của bài viết. Trong 99% trường hợp, sử dụng định nghĩa trước và các hàm thông thường trong file .C/.CPP cũng như không có hàm inline trong file header là đủ.
(còn tiếp)

Nguồn: http://www.gamedev.net/reference/programming/features/orgfiles/page3.asp

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s