2010-01-26 26 views
10

Nó đã cho tôi một thời gian dài để nhận ra tầm quan trọng và tinh tế có biến:C++ mẫu cụ thể do thiết kế ngôn ngữ

1) tồn tại trên stack

2) đã destructors họ gọi khi họ rơi ra của phạm vi

.

Hai điều này cho phép những thứ như:

A) RAII

B) refcounted GC

Thú vị đủ, (1) & (2) không có sẵn trong ngôn ngữ "thấp" như C /Hội,, tổ hợp; cũng như trong các ngôn ngữ "cao hơn" như Ruby/Python/Java (vì GC ngăn chặn việc phá hủy các đối tượng có thể dự đoán được).

Tôi tò mò - những kỹ thuật nào khác mà bạn biết là rất cụ thể về C++, do lựa chọn thiết kế ngôn ngữ.

Cảm ơn!

Chỉnh sửa: Nếu câu trả lời của bạn là "điều này hoạt động trong C++ & ngôn ngữ khác này", điều đó cũng được. Những điều tôi muốn tìm hiểu tương tự như:

Bằng cách chọn không có các tính năng nhất định (như GC), chúng tôi có được các tính năng khác (như RAII + khả năng hủy hoại đối tượng có thể dự đoán được). Trong những lĩnh vực nào của C++, bằng cách chọn KHÔNG có các tính năng mà các ngôn ngữ "cấp cao hơn" khác có, C++ quản lý để có được các mẫu mà các langauges cấp cao hơn không thể diễn đạt.

+0

Tôi không tin C++ có GC, C# không đương nhiên nhưng đó là một con thú khác nhau. – Hogan

+1

Có, quản lý tài nguyên thông qua RAII (có hoặc không có tính toán ref) không thực sự là GC, nó tổng quát hơn nhiều. –

+0

Plus nó xảy ra tại thời điểm xác định trước, GC là không xác định. – Hogan

Trả lời

3

Tôi thực sự yêu các lớp đặc điểm. Không chính xác cụ thể của C++ (các ngôn ngữ khác như Scala có chúng), nhưng nó cho phép bạn thích nghi với các đối tượng, về cơ bản để xác định một tập hợp các hoạt động mà một kiểu nên hỗ trợ. Hãy tưởng tượng rằng bạn muốn có một "hasher", theo ý nghĩa của tr1::hash. băm được định nghĩa cho một số loại, nhưng không được xác định cho các loại khác. Làm thế nào bạn có thể tạo một lớp có băm được xác định cho tất cả các loại mà bạn muốn? Bạn có thể khai báo một lớp học như:

template < typename T> 
struct hashing_tratis 
{ 
    typedef std::tr1::hash<T> hashing_type; 
}; 

có nghĩa là, bạn mong đợi một lớp có hasing_type chính xác được định nghĩa sẽ được sử dụng. Tuy nhiên, băm không được định nghĩa, chẳng hạn, cho myType, vì vậy bạn có thể viết:

template <> 
struct hashing_traits<myType> 
{ 
    typedef class_that_knows_how_to_hash_myType hashing_type; 
}; 

Bằng cách này, giả sử rằng bạn cần một cách để băm bất kỳ loại mà bạn sử dụng trong chương trình của bạn (bao gồm cả myType).Bạn có thể viết một chữ cái "phổ quát" bằng cách tạo một đặc điểm có tính chất:

template <typename T> 
struct something { 
    typename hashing_traits<T>::hashing_type hasher; 
    .... // here hasher is defined for all your relevant types, and knows how to hash them 
+0

Các ngôn ngữ hỗ trợ sự phản chiếu có thể làm được điều này mà không có lớp đặc điểm rườm rà. Họ chỉ hỏi lớp nếu nó hỗ trợ một giao diện và gọi nó. Điều tương tự cũng có thể có trong các ngôn ngữ có hỗ trợ chức năng đặt hàng đầu tiên. – jmucchiello

+1

jmucchiello: Vâng, không chính xác. Điều gì xảy ra, ví dụ, khi bạn yêu cầu một lớp hoặc giao diện mà đối tượng này không thực hiện? Bạn phải "tìm kiếm" một bộ điều hợp. Loại tham số hóa trong C++ cho phép xây dựng đặc điểm này với một giao diện đồng nhất. –

2

Vâng, điều này có thể được thực hiện bằng ngôn ngữ lập trình D, cũng như hai bạn đề cập, nhưng các mẫu đủ mạnh để về cơ bản hoạt động như thời gian biên dịch gõ vịt khá hữu ích. Tôi thường cảm thấy rằng một phần lớn của cuộc tranh luận giữa các ngôn ngữ tĩnh và động có thể được giải quyết bằng một hệ thống mẫu đủ tốt, để mặc dù các kiểu cần được giải quyết tĩnh tại thời gian biên dịch mà chúng không cần phải biết trong thời gian phát triển. C++ đi tiên phong trong ý tưởng này (ít nhất là trong số các ngôn ngữ chính thống). D mất thêm vài bước nữa.

Với các mẫu và cách nhập vịt thời gian biên dịch, bạn có được tốt nhất của cả hai thế giới: Sự linh hoạt không cần phải xác định loại tại thời điểm phát triển của một hàm hoặc lớp và sự an toàn và hiệu suất của việc biết chúng tại thời gian biên dịch.

+0

Câu hỏi này đặc biệt về C++. –

+1

@Neil: Tôi chỉ đang chỉ ra rằng nó không hoàn toàn ** cụ thể, mặc dù C++ là ngôn ngữ phổ biến nhất trong đó kỹ thuật này có sẵn. – dsimcha

1

Vâng, gần như tất cả 'các mẫu thiết kế' có một tie mạnh để C++. Thay vào đó, bạn nên giả định rằng chúng KHÔNG có liên quan đến 'thiết kế phù hợp' và 'các phương pháp hay nhất' và mọi thứ để làm với các tiền tố C++ kỳ lạ và cẩn thận xem xét nhu cầu thực sự của bất kỳ thứ gì thay vì vô tình làm phức tạp mã của bạn. Đặc biệt là vì nhiều mẫu thiết kế là các kiểu bandaid để khắc phục các vấn đề do các mẫu thiết kế khác tạo ra. Điều này áp dụng nhiều gấp mười lần khi sử dụng bất kỳ ngôn ngữ nào so với C++, vì C++ có một số lượng lớn các vấn đề không có ngôn ngữ nào khác.

Ví dụ, singleton là ví dụ rõ ràng nhất về điều này.Lý do thực sự cho điều này là cách mà C++ đã thực hiện rất kém khởi tạo tĩnh. Nếu không có điều này, bạn thực sự phải biết những gì bạn đang làm để làm cho khởi tạo hoạt động đúng, và hầu hết mọi người thực sự không. Đối với hầu hết các ứng dụng, chi phí phụ trội của singleton là tốt, nhưng cần lưu ý rằng nó có chi phí cao, và nó không có sử dụng trong hầu hết các ngôn ngữ. Nó cũng tạo ra các vấn đề phụ trong mỗi lần thực hiện đơn lẻ mà tôi đã thấy.

Tương tự áp dụng cho mẫu cầu, tôi có thể thấy bằng cách sử dụng đơn, nhưng đơn giản là không có lý do để sử dụng mẫu cầu. Nó đi xuống để 'bạn có biết những gì bạn đang làm?'. Nếu vậy, bạn không cần mẫu cầu. Nếu không, bạn có lẽ nên tìm hiểu thêm trước khi cố gắng giải quyết vấn đề nhắc bạn sử dụng mẫu cầu nối. Quan trọng nhất, nó không thực sự hữu ích trong tất cả các ngôn ngữ bên cạnh C++. Điểm của nó là tách riêng việc thực hiện và giao diện, mà trong các ngôn ngữ OO hiện đại hơn được thực hiện đã có bởi vì họ có các mô-đun thích hợp của một số loại. Trong C++, tốt hơn là sử dụng các giao diện ảo thuần túy cho các trường hợp như thế này (và không nghĩ rằng điều này có hiệu suất kém hơn, nó có hiệu suất tốt hơn nhiều so với mô hình cầu kết hợp với các khuôn mẫu).

Nhưng đó là vấn đề với mẫu thiết kế; nó không phải là một phương pháp, chỉ là một loạt những điều ngẫu nhiên. Những điều ngẫu nhiên hầu hết những người ủng hộ họ không thực sự hiểu, hầu hết trong số đó không có nơi nào được gọi là bất cứ điều gì với thiết kế từ trong đó. Chúng không phải là công cụ thiết kế, mà là các giải pháp cụ thể cho các vấn đề khác nhau. Nhiều người trong số đó là tránh được.

Trớ trêu thay, java API có đầy đủ các cầu và nhiều mẫu thiết kế khác hoàn toàn vô nghĩa trong Java. Java API dễ sử dụng hầu hết thời gian, nhưng chế độ thừa kế quá phức tạp có thể rất cồng kềnh để làm việc.

+0

Tại sao nên thực hiện mô hình cầu hoa văn tốt có hiệu suất kém hơn thì một dựa trên vtables? Ngoài ra, các bridge không chỉ là một câu hỏi về các mô-đun, việc ẩn giấu sự phức tạp với cách tiếp cận dựa trên lớp là giá trị chính của nó bất kể ngôn ngữ. –

+0

khoa học máy tính. –

+1

Mức độ gián tiếp bổ sung không ảnh hưởng đến hiệu suất nhiều như nó giúp GRASP thói quen tốt. Và bạn không trả lời câu hỏi. –

2
  • Luôn chắc người gọi trách nhiệm cho bộ nhớ phân bổ/deallocation, có nghĩa là mã mà sẽ trông như thế này trong Java/C#:

    MyClass doSomething(MyClass someInstance); 
    

    trông như thế này trong C/C++:

  • Sử dụng hàm hủy để đóng socket, tập tin xử lý vv
+1

Hoặc 'boost :: shared_ptr doSomething (const MyClass & someInstance);' –

+1

Nếu bạn muốn FORCE người gọi chịu trách nhiệm phân bổ/miễn phí, bạn nên sử dụng tài liệu tham khảo: void doSomething (const myclass & in_param, myclass & out_param); Bạn không thể gọi doSomething với việc phân bổ out_param ở đâu đó và chuyển nó vào. – jmucchiello

+0

Thực ra, thực hành tốt hơn là hoàn toàn chịu trách nhiệm cho tất cả phân bổ/dealloc mã của bạn nên sử dụng. Bạn có thể làm điều này bằng cách (1) sử dụng con trỏ thông minh sẽ giải phóng tài nguyên được phân bổ của bạn khi không được sử dụng và (2) bằng cách trả về một đối tượng bằng cách sao chép và làm cho bản sao đó miễn phí tài nguyên khi bị hủy. –

0

Ít ngôn ngữ hỗ trợ multiple dispatch (với double dispatch là phổ biến nhất) như C++, bạn có thể làm điều đó một cách tĩnh hoặc động.
Với nó, bạn có thể cho phép các cá thể tương tác với nhau mà không biết hoặc thử nghiệm, loại bê tông của chúng.

+0

Tôi sẽ nói gần như tất cả các ngôn ngữ (ngoại trừ một số giống như CLOS hỗ trợ nó nguyên bản) hỗ trợ nó như C++ không - bạn phải tự viết mã cho nó. –

+0

C# 4.0 hiện nó! – Hogan

+0

@Hogan: Ồ, tôi phải nhìn nó lên. Chỉ tự động mặc dù tôi đoán? –

1

Có thành ngữ pImpl. Nó hoạt động tương tự như mẫu Bridge.

Expression Template, làm chậm trễ các đánh giá biểu thức của C++.

Hai tác phẩm cũng khám phá câu hỏi này: More C++ IdiomsEffective C++ (Scott Meyers, 2005)

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