2012-07-03 38 views
7

Tôi muốn xác định một loại mới trong C++ mà chỉ là một số loại nguyên thủy (trong ví dụ của tôi là int, có thể là bất kỳ loại nào). Tôi gọi kiểu NodeId trong ví dụ này.Hiệu suất: typedef vs lớp trình bao bọc cho các kiểu nguyên thủy?

Tôi chỉ có thể sử dụng typedef int NodeId. Tôi muốn có giá trị mặc định cho NodeId giây, vì vậy tôi sẽ sử dụng #define NULL_NODE_ID -1.

Bây giờ, tôi nghĩ rằng nó sẽ đẹp hơn để xác định một lớp thay vì typedef để cho phép một hàm isValid() và một constructor mặc định xây dựng một null NodeId:

class NodeId 
{ 
    int value; 
public: 
    inline NodeId() : value(-1) {} 
    inline NodeId(int value) : value(value) {} 
    inline operator int() {return value;} 
    inline bool isValid() {return value != -1;} 
    //... 
}; 

Có bất kỳ nhược điểm hiệu suất mà kết quả trong việc sử dụng phương pháp thứ hai?

+0

Nếu bạn sử dụng một trình biên dịch mới, thì nó phải giống như sử dụng int. – mfontanini

+0

@mfontanini Vậy tại sao bạn lại downvote câu hỏi? Đây là câu trả lời cho câu hỏi, bạn nên đăng nó như vậy. – leemes

+0

@leemes tại sao bạn lại cho rằng đó là tôi? – mfontanini

Trả lời

6

Trên thực tế, có hai lý do này hình dung có thể chậm hơn.

Trước tiên, không có cách nào để tạo NodeId chưa được khởi tạo. Thông thường, đó là điều tốt. Nhưng hãy tưởng tượng bạn có mã như sau:

NodeId nodeid; 
foo.initializeNodeId(&nodeid); 

Bạn sẽ thực hiện một nhiệm vụ bổ sung không thực sự cần thiết.

Bạn có thể khắc phục điều đó bằng cách thêm một hàm tạo đặc biệt. Nó có thể tốt hơn nhiều để tạo ra một Foo :: createNodeId() vì vậy bạn không cần Foo :: initializeNodeId (& NodeId), nhưng nếu bạn không kiểm soát định nghĩa của Foo, điều đó có thể không thực hiện được.

Thứ hai, một NodeId không phải là biểu thức hằng số biên dịch. Như dasblinkenlight cho thấy, điều này có nhiều khả năng gây ra vấn đề với mã không hợp pháp hơn là gây ra các vấn đề về hiệu suất, nhưng cả hai đều có thể. (Tại sao? Bởi vì bạn có thể buộc trình biên dịch chèn mã để làm một số phép tính trong thời gian chạy mà có thể đã được thực hiện tại thời gian biên dịch, nếu bạn đang sử dụng một int.Không phải là điều này có thể là một vấn đề cho một lớp được gọi là NodeId…)

May mắn thay, nếu bạn đang sử dụng C++ 11, bạn có thể sửa lỗi đó bằng constexpr. Và nếu bạn muốn mã của bạn cũng hợp pháp C++ 03, bạn có thể xử lý nó bằng macro.

Ngoài ra, như được chỉ ra bởi dasblinkenlight, bạn đang thiếu const theo hai phương pháp.

Cuối cùng, không có lý do gì để viết "nội tuyến" trên các phương thức được xác định bên trong định nghĩa lớp; chúng vốn đã nội tuyến.

Đưa nó tất cả cùng nhau:

#if __cplusplus > 201000L 
#define CONSTEXPR_ constexpr 
#else 
#define CONSTEXPR_ 
#endif 

class NodeId 
{ 
    int value; 
public: 
    struct Uninitialized {}; 
    CONSTEXPR_ NodeId() : value(-1) {} 
    CONSTEXPR_ NodeId(Uninitialized) {}  
    CONSTEXPR_ NodeId(int value) : value(value) {} 
    CONSTEXPR_ operator int() const {return value;} 
    CONSTEXPR_ bool isValid() const {return value != -1;} 
    //... 
}; 

Bây giờ bạn có thể làm điều này, để tránh những chi phí thêm một -1 chuyển nhượng.

NodeId nodeId(NodeId::Uninitialized()); 
foo.initializeNodeId(&nodeid); 

Và điều này, sử dụng một cách hợp pháp một nodeID như một tổ chức phi kiểu mẫu tham số:

myClassTemplate<NodeId(3)> c; 

Hoặc, điều này, chắc chắn trình biên dịch có thể hợp pháp chỉ khởi tạo x 4:

int x = 3; 
x += NodeId(1); 
+0

Cảm ơn câu trả lời tuyệt vời đó :) – Misch

+0

abarnert, có khả năng hỗ trợ số học số nguyên trong hộp không? Nút 'NodeId; node ++; 'không biên dịch. Tôi phải thực hiện 'toán tử ++' một cách thủ công. Điều này có thể thực hiện được nếu không thực hiện thủ công các toán tử tầm thường đó không? – leemes

+0

@leemes: Không, không hoàn toàn tự động. Nhưng boost.operator có thể giúp bạn xác định một số rõ ràng và nhận người khác miễn phí. Nếu bạn đang thực hiện rất nhiều các lớp này, bạn có thể sử dụng CRTP, một lớp cơ sở hoặc trình tạo mã để ít nhất bạn chỉ phải xác định tất cả các toán tử một lần. Hoặc xác định một mẫu lớp "SmartInt" đơn, và sau đó làm một cái gì đó như "struct NodeIdTag {}; typedef SmartInt NodeId;" (tất nhiên bạn có thể gói gọn trong một macro). – abarnert

4

Nếu bạn sử dụng trình biên dịch tương đối mới, thì mã được tạo sẽ giống nhau. Trình biên dịch sẽ không có vấn đề gì trong các hàm thành viên đó. Trong trường hợp bạn thực sự có nghi ngờ về nó, bạn luôn có thể tháo rời tệp thực thi.

+0

Điều đó không đúng. 'int' là kiểu POD, nhưng' NodeId' thì không. Trình biên dịch sẽ tạo ra mã khởi tạo bổ sung nơi mà điều này được sử dụng, bạn bị cấm sử dụng nó trong các công đoàn, và nó sẽ 'lây nhiễm' các lớp khác với không PODness của nó. –

+0

'NodeId x;' sẽ tạo cùng một mã như "int x (-1);", vì đó là cách OP sẽ sử dụng loại đó nếu nó là 'int'. Tuy nhiên, đối số không phải POD là đúng. – mfontanini

+0

@DanHulme C++ 11 [cho phép] (https://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions) không tập hợp trong các công đoàn – Praetorian

3

Nếu cài đặt tối ưu hóa trình biên dịch cho phép trình biên dịch nội tuyến mọi thứ, sẽ không có sự khác biệt về hiệu suất. Bất lợi duy nhất mà tôi có thể phát hiện là các biểu thức dựa trên các đối tượng của lớp này có thể không còn đủ điều kiện như các hằng số biên dịch thời gian, mà có thể quan trọng trong lập trình mẫu. Khác hơn thế, bạn kết thúc với sự rõ ràng bổ sung mà không tốn chi phí thời gian chạy.


P.S. Bạn đang thiếu const trong operator int của bạn và isValid:

inline operator int() const {return value;} 
inline bool isValid() const {return value != -1;} 
+1

"biên dịch * thời gian * biểu thức liên tục *", có lẽ? –

+1

bạn đúng về const, tôi vừa mới hack ví dụ này để đăng nó ở đây. – Misch

1

Nếu bạn đang lập kế hoạch thực hiện điều này thành một thư viện có thể được sử dụng bởi một số ngôn ngữ lập trình khác (như nếu bạn đang viết điều này vào một DLL C++) có lợi thế nhất định để có nó typedef int.

Ví dụ: khi bạn đang viết trình bao bọc python hoặc trình bao bọc java cho API của mình, bạn không có nhiều nhức đầu để lớp và loại của bạn chuyển sang. Sau đó, tất cả những gì bạn phải lo lắng là kích thước bit của int.

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