2010-03-01 52 views
51

Tôi đang sử dụng shared_ptr và STL rộng rãi trong một dự án và điều này dẫn đến các loại quá dài, dễ bị lỗi như shared_ptr< vector< shared_ptr<const Foo> > > (Tôi là nhà lập trình ObjC theo sở thích) nó sẽ rõ ràng hơn nhiều, tôi tin rằng, để luôn gọi đây là FooListPtr và ghi lại quy ước đặt tên là "Ptr" có nghĩa là shared_ptr và "List" có nghĩa là vectơ của shared_ptr.Thực hành tốt nhất cho tệp typedefs

Điều này rất dễ dàng để gõ, nhưng nó gây ra nhức đầu với các tiêu đề. Dường như tôi có một số tùy chọn về vị trí để xác định FooListPtr:

  • Foo.h. Điều đó bao gồm tất cả các tiêu đề và tạo ra các vấn đề xây dựng nghiêm trọng, do đó, nó không phải là một khởi đầu.
  • FooFwd.h ("tiêu đề tiến lên"). Đây là những gì Hiệu quả C++ gợi ý, dựa trên iosfwd.h. Nó rất nhất quán, nhưng chi phí của việc duy trì gấp đôi số lượng tiêu đề có vẻ gây phiền nhiễu nhất.
  • Common.h (đặt tất cả chúng lại với nhau thành một tệp). Điều này giết chết reusability bởi entwining rất nhiều loại không liên quan. Bây giờ bạn không thể chỉ nhận một đối tượng và di chuyển nó đến một dự án khác. Đó là một không khởi động.
  • Một số loại ma thuật #define ưa thích mà typedef nếu nó chưa được đánh máy. Tôi có một sự không thích hợp với người tiền xử lý bởi vì tôi nghĩ rằng nó làm cho người mới khó có thể mò mẫm mã, nhưng có thể ...
  • Sử dụng phân lớp vectơ thay vì typedef. Điều này có vẻ nguy hiểm ...

Có các phương pháp hay nhất ở đây không? Làm thế nào để họ bật ra trong mã thực, khi reusability, dễ đọc và nhất quán là tối thượng?

Tôi đã đánh dấu wiki cộng đồng này nếu người khác muốn thêm các tùy chọn bổ sung để thảo luận.

+5

Tôi có thể hỏi tại sao câu hỏi này là một wiki cộng đồng không? –

+0

@Konrad, nếu có các đề xuất khác, tôi đã đề xuất thêm chúng vào danh sách để giúp người đọc sau này dễ dàng xem các tùy chọn khác biệt với câu trả lời về thành tích của họ. Có lẽ wiki cộng đồng được sử dụng khác nhau? –

+1

Và sau nhiều nghiên cứu, tôi đã khám phá lại những gì tôi đã khám phá lần cuối khi tôi nhấp vào wiki cộng đồng, điều đó không có nghĩa là tôi làm điều đó ... Hy vọng tôi đã học được bài học của mình lần này. –

Trả lời

6

Tôi sẽ đi với một cách tiếp cận kết hợp của tiêu đề chuyển tiếp và loại tiêu đề common.h dành riêng cho dự án của bạn và chỉ bao gồm tất cả các tiêu đề khai báo chuyển tiếp và bất kỳ nội dung nào khác phổ biến và nhẹ.

Bạn phàn nàn về chi phí của việc duy trì gấp đôi số lượng tiêu đề nhưng tôi không cho rằng điều này là quá nhiều vấn đề: tiêu đề chuyển tiếp thường chỉ cần biết số lượng rất hạn chế (một?), và đôi khi không phải là loại đầy đủ.

Bạn thậm chí có thể thử tự động tạo tiêu đề bằng cách sử dụng tập lệnh (điều này được thực hiện, ví dụ: SeqAn) nếu có thực sự là có nhiều tiêu đề.

+0

Tôi làm việc chủ yếu trên mã được sử dụng bởi một số dự án, vì vậy hầu như không có điều này cụ thể cho một dự án duy nhất. Một "common.h" duy nhất làm cho nó rất khó cho nhiều dự án để tái sử dụng các thành phần. –

+0

@Konrad: SeqAn có sử dụng thư viện tăng cường và std không? Kịch bản bạn đề cập đến là gì? –

+0

@ Benoît: SeqAn sử dụng STL nhưng (không may, IMHO) không có thư viện Boost. Đối với kịch bản lệnh, nó chỉ phân tích tất cả các tệp tiêu đề (nhưng sau đó, SeqAn chỉ là tiêu đề) và tạo một tệp lớn chứa các khai báo chuyển tiếp của tất cả các loại và hàm được xuất. –

1

Thật không may với typedefs bạn phải chọn giữa các tùy chọn không lý tưởng cho tệp tiêu đề của bạn. Có những trường hợp đặc biệt khi tùy chọn một (ngay trong tiêu đề lớp) hoạt động tốt, nhưng có vẻ như nó sẽ không hoạt động cho bạn. Cũng có những trường hợp tùy chọn cuối cùng hoạt động tốt, nhưng thường là nơi bạn đang sử dụng lớp con để thay thế một mẫu liên quan đến một lớp với một thành viên duy nhất của loại std :: vector. Đối với trường hợp của bạn, tôi sẽ sử dụng giải pháp tiêu đề khai báo chuyển tiếp. Có thêm đánh máy và trên không, nhưng nó sẽ không phải là C++ nếu không, phải không? Nó giữ mọi thứ riêng biệt, sạch sẽ và nhanh chóng.

+0

Nó dường như là cách tiếp cận nhất quán và có khả năng mở rộng. "nó sẽ không là C++ nếu không ...." Nó dường như là cách. Tôi là một lập trình viên ObjC thường. Mọi người phàn nàn về những cái tên dài trong ObjC, nhưng tôi thấy chúng rất thoải mái và không quan tâm đến chúng. Nhưng trong C++ tôi cảm thấy tôi dành một nửa thời gian làm công việc bận rộn, hoặc làm cho mã không phù hợp và khó duy trì để tránh việc gõ thêm. –

4

+1 để ghi lại các quy ước typedef.

  • foo.h - có thể giúp bạn chi tiết những vấn đề bạn có với điều đó?
  • FooFwd.h - Tôi sẽ không sử dụng chúng nói chung, chỉ trên "điểm nóng hiển nhiên". (Có, "điểm nóng" khó xác định). Nó không thay đổi các quy tắc IMO bởi vì khi bạn giới thiệu một tiêu đề fwd, các typedefs liên quan từ foo.h di chuyển đến đó.
  • Common.h - làm mát cho các dự án nhỏ, nhưng không mở rộng quy mô, tôi đồng ý. !
  • Một số loại #define ưa thích ... PLEASE NO ...
  • Sử dụng một lớp con vector - không làm cho nó tốt hơn. Tuy nhiên, bạn có thể sử dụng ngăn chặn.

Vì vậy, đây là gợi ý prelimenary (sửa đổi từ đó câu hỏi khác ..)

  1. header Standard <boost/shared_ptr.hpp>, <vector> vv có thể đi vào một tiêu đề precompiled/tiết lộ bao gồm tập tin cho dự án. Đây không phải là xấu. (Cá nhân tôi vẫn bao gồm chúng khi cần thiết, nhưng điều đó hoạt động ngoài việc đưa chúng vào PCH.)

  2. Nếu vùng chứa là chi tiết triển khai, typedef sẽ chuyển đến nơi container được khai báo (ví dụ: container là thành viên lớp tư nhân)

  3. loại Associated (như FooListPtr) đi đến nơi Foo được declarated, nếu loại liên quan là việc sử dụng chính của các loại. Điều đó hầu như luôn đúng đối với một số loại - ví dụ: shared_ptr.

  4. Nếu Foo nhận tiêu đề khai báo chuyển tiếp riêng biệt và loại được liên kết tương ứng với tiêu đề đó, nó cũng chuyển sang FooFwd.h.

  5. Nếu loại chỉ được liên kết với một giao diện cụ thể (ví dụ: tham số cho phương thức công khai), nó sẽ xuất hiện ở đó.

  6. Nếu loại được chia sẻ (và không đáp ứng bất kỳ tiêu chí nào trước đây), nó sẽ có tiêu đề riêng. Lưu ý rằng điều này cũng có nghĩa là kéo tất cả các phụ thuộc.

Nó cảm thấy "hiển nhiên" đối với tôi, nhưng tôi đồng ý nó không tốt như tiêu chuẩn mã hóa.

+0

điểm tốt nói chung, nhưng lại # 1: tôi không khuyên bạn nên bao gồm rất nhiều thư viện chuẩn trong một tiêu đề chung. hành vi phạm tội tồi tệ nhất là lượng dữ liệu tĩnh và định nghĩa được tạo ra. – justin

+0

@Justin - Định nghĩa - đã đồng ý, nhưng đó là những tiêu đề được biên dịch sẵn. nhưng dữ liệu tĩnh riêng tư? gần như không có gì trong tiêu đề chuẩn (vì nó sẽ là đơn vị dịch thuật). Hay bạn có nghĩa là các vấn đề với việc triển khai hỗ trợ mẫu sớm? Trừ khi đó là một vấn đề được biết đến và đã được chứng minh cho trình biên dịch của bạn, không có điểm nào trong việc gắn bó với nó. – peterchen

+0

tôi không sử dụng các tiêu đề được biên dịch trước. lý do: không đủ phổ biến và tôi thường sử dụng các bản dựng kết hợp để có nhiều trường hợp một tệp được biên dịch cho mỗi gói. Ngoài ra, các tiêu đề được biên dịch trước không quy mô đặc biệt tốt trong các hệ thống xây dựng phân tán. theo như dữ liệu tĩnh, một khai báo hiển nhiên là các luồng đầu vào/đầu ra (được bao gồm bởi 'iostream'). tôi mất khoảng 20% ​​của một số nhị phân * sau khi * loại bỏ không cần thiết bao gồm từ các thư viện chuẩn (được đo bằng cách sử dụng triển khai tương đối hiện đại các thư viện std được gửi kèm với gcc của apple). các vấn đề rõ ràng vượt quá kích thước nhị phân. – justin

13

Tôi đang lập trình trên một dự án có vẻ như nó sử dụng phương pháp common.h. Nó hoạt động rất tốt cho dự án đó.

Có một tệp có tên ForwardsDecl.h nằm trong tiêu đề được biên dịch trước và chỉ đơn giản là chuyển tiếp tất cả các lớp quan trọng và typedef cần thiết. Trong trường hợp này, unique_ptr được sử dụng thay vì shared_ptr, nhưng việc sử dụng phải giống nhau. Nó trông giống như thế này:

// Forward declarations 
class ObjectA; 
class ObjectB; 
class ObjectC; 

// List typedefs 
typedef std::vector<std::unique_ptr<ObjectA>> ObjectAList; 
typedef std::vector<std::unique_ptr<ObjectB>> ObjectBList; 
typedef std::vector<std::unique_ptr<ObjectC>> ObjectCList; 

Mã này được chấp nhận bởi Visual C++ 2010 mặc dù các lớp học chỉ được hướng tới tương lai tuyên bố (các định nghĩa lớp đầy đủ thì không cần thiết nên không cần thiết phải bao gồm tập tin tiêu đề của nhau lớp).Tôi không biết nếu đó là tiêu chuẩn và trình biên dịch khác sẽ yêu cầu định nghĩa lớp đầy đủ, nhưng nó hữu ích mà nó không: một lớp khác (ObjectD) có thể có một ObjectAList như một thành viên, mà không cần phải bao gồm ObjectA.h - điều này có thể thực sự giúp giảm phụ thuộc tập tin tiêu đề! Bảo trì không phải là vấn đề đặc biệt, bởi vì các khai báo chuyển tiếp chỉ cần phải được viết một lần và bất kỳ thay đổi tiếp theo nào chỉ cần xảy ra trong khai báo đầy đủ trong tệp tiêu đề của lớp (và điều này sẽ kích hoạt ít tệp nguồn hơn). biên dịch lại do phụ thuộc giảm). Cuối cùng nó xuất hiện này có thể được chia sẻ giữa các dự án (tôi đã không cố gắng bản thân mình) bởi vì ngay cả khi một dự án không thực sự tuyên bố một ObjectA, nó không quan trọng bởi vì nó chỉ được chuyển tiếp khai báo và nếu bạn không sử dụng nó trình biên dịch không quan tâm. Do đó, tệp có thể chứa tên của các lớp trên tất cả các dự án mà nó được sử dụng và không quan trọng nếu một số dự án bị thiếu cho một dự án cụ thể. Tất cả những gì được yêu cầu là tiêu đề khai báo đầy đủ cần thiết (ví dụ: ObjectA.h) được bao gồm trong bất kỳ tệp nào nguồn (.cpp) mà thực sự sử dụng chúng.

+0

Điều này rất giống với cách tôi tổ chức các thư viện C++ của mình. Điểm của headerfile khai báo chuyển tiếp là nó sẽ khai báo tất cả các lớp và các kiểu con trỏ trong thư viện. Bằng cách này, bạn chỉ cần một tiêu đề như vậy, không phải một tiêu đề cho mỗi lớp. Xem http://stackoverflow.com/questions/3935183/forward-declaration-include-on-top-of-declaration-include-classfwd-h-class-h/3935468#3935468 để biết thêm chi tiết. –

2

Tôi đang sử dụng shared_ptr và STL rộng rãi trong một dự án, và điều này dẫn đến quá lâu, các loại dễ bị lỗi như shared_ptr < vector < shared_ptr>> (Tôi là một lập trình viên ObjC bởi sở thích, nơi tên dài là tiêu chuẩn, và vẫn còn đây là quá nhiều.) Nó sẽ rõ ràng hơn nhiều, tôi tin rằng, để liên tục gọi FooListPtr này và ghi lại quy ước đặt tên là "Ptr" có nghĩa là shared_ptr và "List" có nghĩa là vector của shared_ptr.

để bắt đầu, tôi khuyên bạn nên sử dụng các cấu trúc thiết kế tốt cho phạm vi (ví dụ: không gian tên) cũng như các tên mô tả, không viết tắt cho typedef. FooListPtr cực kỳ ngắn, imo. không ai muốn đoán những gì viết tắt có nghĩa là (hoặc ngạc nhiên khi tìm thấy Foo là const, chia sẻ, vv), và không ai muốn thay đổi mã của họ chỉ đơn giản là vì va chạm phạm vi.

cũng có thể giúp chọn tiền tố cho typedef trong thư viện của bạn (cũng như các danh mục phổ biến khác).

nó cũng là một ý tưởng tồi để kéo loại ra khỏi phạm vi tuyên bố của họ:

namespace MON { 
namespace Diddy { 
class Foo; 
} /* << Diddy */ 

/*...*/ 
typedef Diddy::Foo Diddy_Foo; 

} /* << MON */ 

có những ngoại lệ như sau:

  • một kiểu riêng hoàn toàn ecapsualted
  • một loại chứa trong một phạm vi mới

trong khi chúng tôi đang ở đó, using trong phạm vi không gian tên và bí danh không gian tên cần tránh - đủ điều kiện phạm vi nếu bạn muốn giảm thiểu bảo trì trong tương lai.

Điều này rất dễ typedef, nhưng nó gây ra đau đầu với các tiêu đề. Dường như tôi có một số tùy chọn để xác định FooListPtr:

Foo.h. Điều đó bao gồm tất cả các tiêu đề và tạo ra các vấn đề xây dựng nghiêm trọng, do đó, nó không phải là một khởi đầu.

nó có thể là tùy chọn cho khai báo thực sự phụ thuộc vào các khai báo khác. ngụ ý rằng bạn cần chia các gói, hoặc có một giao diện chung, được bản địa hoá cho các hệ thống con.

FooFwd.h ("tiêu đề tiến lên"). Đây là những gì hiệu quả C++ cho thấy, dựa trên iosfwd.h. Nó rất nhất quán, nhưng chi phí của việc duy trì gấp đôi số lượng tiêu đề có vẻ gây phiền nhiễu nhất.

đừng lo lắng về việc duy trì điều này, thực sự. đó là một thực hành tốt. trình biên dịch sử dụng các khai báo về phía trước và typedef với rất ít nỗ lực. nó không gây phiền nhiễu vì nó giúp giảm sự phụ thuộc của bạn, và giúp đảm bảo rằng tất cả chúng đều chính xác và có thể nhìn thấy. có thực sự không phải là nhiều hơn để duy trì kể từ khi các tập tin khác đề cập đến tiêu đề 'loại gói'.

Common.h (đặt tất cả chúng lại với nhau thành một tệp). Điều này giết chết reusability bởi entwining rất nhiều loại không liên quan. Bây giờ bạn không thể chỉ nhận một đối tượng và di chuyển nó đến một dự án khác. Đó là một không khởi động.

gói phụ thuộc và bao gồm là tuyệt vời (lý tưởng, thực sự) - không loại trừ điều này. bạn rõ ràng sẽ phải tạo các giao diện gói (hoặc các thư viện) được thiết kế và cấu trúc tốt, và đại diện cho các lớp liên quan của các thành phần. bạn đang tạo ra một vấn đề không cần thiết trong việc tái sử dụng đối tượng/thành phần. giảm thiểu các dữ liệu tĩnh của một thư viện, và để cho các liên kết và các giai đoạn dải làm công việc của họ. một lần nữa, giữ cho các gói của bạn nhỏ và tái sử dụng và điều này sẽ không là một vấn đề (giả sử các thư viện/gói của bạn được thiết kế tốt).

Một số loại ma thuật #define ưa thích mà typedef nếu nó chưa được đánh máy. Tôi có một điều không thích hợp cho người tiền xử lý bởi vì tôi nghĩ rằng nó làm cho người mới khó có thể mò mẫm mã, nhưng có lẽ ...

thực sự, bạn có thể khai báo một kiểu gõ trong cùng phạm vi nhiều lần (ví dụ , trong hai tiêu đề riêng biệt) - đó không phải là lỗi.

tuyên bố typedef trong cùng phạm vi với các loại cơ bản khác nhau là lỗi. chắc chắn. bạn phải tránh điều này, và may mắn là trình biên dịch thực thi điều đó.

để tránh điều này, tạo 'bản dịch xây dựng' bao gồm cả thế giới - trình biên dịch sẽ gắn cờ khai báo các kiểu gõ không phù hợp.

cố gắng lén lút bằng typedef và số ít nhất là (hoặc đủ để giải phóng miễn phí) không đáng để thử. đôi khi bạn sẽ cần một loạt các hỗ trợ có điều kiện cho các tờ khai chuyển tiếp - một khi được xác định, nó rất dễ dàng (các thư viện stl là một ví dụ tốt về điều này - trong trường hợp bạn cũng đang chuyển tiếp khai báo template<typename,typename>class vector;).

cách tốt nhất là chỉ cần có tất cả các khai báo này để xem bất kỳ lỗi nào ngay lập tức và bạn có thể tránh bộ tiền xử lý trong trường hợp này làm tiền thưởng.

Sử dụng phân lớp vectơ thay vì typedef. Điều này có vẻ nguy hiểm ...

một phân lớp của std::vector thường được gắn cờ là "lỗi của người mới bắt đầu". vùng chứa này không có nghĩa là được phân lớp. không sử dụng các thực hành không tốt chỉ để giảm thời gian biên dịch/phụ thuộc của bạn.nếu phụ thuộc thực sự là có ý nghĩa, có lẽ bạn nên sử dụng pImpl, anyways:

// <package>.types.hpp 
namespace MON { 
class FooListPtr; 
} 

// FooListPtr.hpp 
namespace MON { 
class FooListPtr { 
    /* ... */ 
private: 
    shared_ptr< vector< shared_ptr<const Foo> > > d_data; 
}; 
} 

Có thực hành tốt nhất đây? Làm thế nào để họ bật ra trong mã thực, khi reusability, dễ đọc và nhất quán là tối thượng?

cuối cùng, tôi đã tìm thấy một gói ngắn gọn dựa trên phương pháp tiếp cận tốt nhất để sử dụng lại, để giảm thời gian biên dịch và giảm thiểu sự phụ thuộc.

Các vấn đề liên quan