2011-11-12 32 views
6

Ví dụ: chúng tôi có chức năng mã hóa. thực hành tốt nhất để sử dụng là gì:tham chiếu hoặc trả lại - thực hành tốt nhất

void Crypto::encoding(string &input, string &output) 
{ 
    //encoding string 
    output = encoded_string; 
} 

hoặc

string Crypto::encoding(string &input) 
{ 
    //encoding string 
    return encoded_string; 
} 

Chúng ta có nên sử dụng tài liệu tham khảo hoặc trả lại trả lại chuỗi? Theo như tôi biết trả về một chuỗi sẽ mất một thời gian để khởi tạo một chuỗi mới sẽ được trả về bởi lệnh trả về. Khi làm việc trên một biến tham chiếu, tôi không lãng phí thời gian để khởi tạo một số biến mới mà tôi chỉ kết thúc hàm.

Chúng ta có nên sử dụng tham chiếu chủ yếu và tạo ra kiểu trả về hàm không? Hoặc chúng ta chỉ nên trả về dữ liệu theo tham chiếu khi chúng ta muốn trả về hai hoặc nhiều biến và khi chúng ta cần trả về một biến thì sử dụng lệnh return?

+0

Vui lòng sử dụng nút '{}' trong trình chỉnh sửa để định dạng mã. – Mat

+7

Bạn có * nghiêm túc * lo lắng về những "lần" bạn nói đến không? Bạn đã làm hồ sơ và xác định rằng thời gian xây dựng chuỗi là nút cổ chai của ứng dụng của bạn? –

Trả lời

1

Với tiêu chuẩn C++ 11 mới, bạn có thể sử dụng biến thể thứ hai do ngữ nghĩa di chuyển mới.

Có thể, trình biên dịch của bạn vẫn chỉ hỗ trợ chuẩn cũ hơn. Trong trường hợp này, ví dụ đầu tiên của bạn không kích động bất kỳ việc sao chép nào và tốt hơn.

+0

nghe giống như tối ưu hóa vi mô –

+1

@VJo: Không thực sự, Nếu chuỗi của bạn có hàng triệu ký tự hoặc nếu bạn gọi hàm này hàng triệu lần thì bạn chỉ phải chạm một nửa bộ nhớ trong ví dụ đầu tiên so với thứ hai. Đó là một trong những lý do tại sao ngữ nghĩa di chuyển được giới thiệu ngay từ đầu. – Manuel

+4

Thứ hai có lẽ cũng không gọi sao chép, xem RVO/NRVO hoặc http://stackoverflow.com/q/1394229/79455 – rve

6

Sao chép mọi thứ thường bị loại bỏ vì hầu hết các trình biên dịch hiện đại đều có các tính năng RVO. Bạn có thể lấy những lợi ích ngay cả khi không có C++ 11.

9

Không tối ưu hóa những gì bạn không đo lường.

Thông thường, tốt hơn (dễ đọc hơn) để trả về kết quả tính toán của bạn với return. Nếu điều này mất nhiều thời gian vì đối tượng quá béo, bạn vẫn có thể hoàn nguyên để trả lại kết quả của mình thông qua tham số tham chiếu, nhưng chỉ sau khi bạn chứng minh rằng điều này sẽ dẫn đến cải thiện đáng kể hiệu suất (đo lường). Ví dụ: nếu bạn chỉ mã hóa các chuỗi rất ngắn và chỉ thực hiện điều đó một lần trong một thời gian, thì chi phí sao chép không đáng kể.

+2

+1 cho * Không tối ưu hóa những gì bạn không đo *, Scott Meyers trích dẫn về Tối ưu hóa sẽ có ở đây "Bạn cần xác định 20% mã của bạn chạy 90% thời gian chạy và sau đó thử tối ưu hóa 20% mã của bạn. " Đừng nhớ những từ chính xác, nhưng có ý nghĩa dự định là như nhau. –

+0

Tôi nghĩ là 10%, không phải 20% :-) – rve

+1

Nói về đo lường, trên g ++ 4.3, hai chức năng chạy ở tốc độ giống hệt nhau. http://ideone.com/sgl9W –

2

Nếu trình biên dịch của bạn hỗ trợ chuẩn C++ 11 và r-value references thì trả về một chuỗi :: string by value thực sự khá hiệu quả. Trước khi tính năng này, các câu trả lời có thể khác một chút vì bạn chỉ dựa vào trình biên dịch thực hiện RVO.

Tôi muốn nói rằng việc sử dụng giá trị trả về có thể tự nhiên hơn và cũng có nghĩa là bạn có thể gán kết quả cho biến cục bộ không đổi hoặc thành viên nhóm để tránh sửa đổi ngẫu nhiên, ví dụ:

const std::string result = crypo.encoding("blah"); 

Hoặc

class SomeClass 
{ 
public: 
    Someclass(Crypto& crypto, const std::string& input) : 
     m_output(crypo.encoding(input)) 
    { 
    } 

private: 
    const std::string m_output; 
}; 

Chỉ cần đảm bảo rằng bạn không trở lại theo giá trị const vì điều này sẽ ức chế ngữ nghĩa di chuyển.

1

Tôi sẽ tiếp tục ghi như đã nói: có thể không phải là một.

encode của bạn trông rất giống với thuật toán có thể/nên là một thuật toán chung thực sự nên sử dụng trình vòng lặp thay vì xử lý trực tiếp bằng chuỗi.

template <class InputIterator, class OutputIterator> 
void encode(InputIterator begin, InputIterator end, OutputIterator result) { 
    while (begin!=end) 
     *result++ = encode_byte(*begin++); 
} 

Bằng cách này bạn có thể (ví dụ) một cách dễ dàng tái sử dụng chính xác cùng một mã để mã hóa dữ liệu trực tiếp từ một input stream (thông qua một std::istream_iterator) tới luồng ra (thông qua một std::ostream_iterator).

Điều này cũng thường loại bỏ hầu hết các câu hỏi về hiệu quả.

+0

Chỉ cần tự hỏi: Nếu tôi nạp 'Sammich :: Iterator' vào hàm của bạn, làm thế nào để 'encode_byte()' biết cách mã hóa một sammich? Bạn cũng cần phải cung cấp thuật toán mã hóa để thực sự là chung chung, nhưng tất cả những gì còn lại từ hàm của bạn là lặp lại vì vậy nó nên được đặt tên là 'for_each' ... ;-) – EricSchaefer

+0

@EricSchaefer: rõ ràng là không tự động có được kiến ​​thức đặc biệt về cách mã hóa mọi loại đầu vào có thể. Tuy nhiên, nó có thể là (chủ yếu) độc lập với loại container chứa dữ liệu đó. –

+0

Chắc chắn đó chỉ là std :: biến đổi bạn đang mô tả? –

2

Tôi sử dụng tài liệu tham khảo. Điều đó cho phép người triển khai thực hiện và trừu tượng lựa chọn, mà không đánh thuế khách hàng nhiều (một số trường hợp quan trọng, một số sẽ không).

Tôi cũng sử dụng chúng cho phong cách nhất quán - Tôi không thích nhìn thấy giao diện công cộng đi qua chi tiết triển khai của chúng.

Giao dịch chuyển tiếp và bản sao có thể tốn kém - nó thay đổi rất nhiều theo loại bạn đang chuyển. Để trở về theo giá trị cho biết loại nên được trivially constructible, swappable, copyable, movable. Trình biên dịch có thể thực hiện một số tối ưu hóa tuyệt vời trong lĩnh vực này (RVO/di chuyển), nhưng bạn cũng có thể đưa ra các quyết định sáng suốt để giảm thiểu các hoạt động tốn kém trong việc triển khai của bạn. Một khi bạn không còn sử dụng các loại, mọi người đều biết các đặc điểm sao chép, sau đó chọn cách trở về trở nên rất phức tạp, vì vậy tôi chỉ giữ cho nó đơn giản và ưu tiên các tham chiếu.

Việc chuyển tham chiếu có một vài lợi ích khác, chẳng hạn như khi khách hàng muốn sử dụng lớp con của loại được chuyển.

Một lợi ích khác nếu bạn cần một chương trình được tối ưu hóa: Tôi thường sẽ xóa bản sao ctor và operator= nếu chúng không nhỏ hoặc có thể. Đi qua tham chiếu có thể thay đổi cho phép bạn làm việc với các loại không được sao chép/gán.

Trong phạm vi nghiêm ngặt của std::string được sử dụng trong câu hỏi này: Trả lại giá trị std::string là khá phổ biến và nhiều tối ưu hóa đã được thực hiện cụ thể cho trường hợp này - RVO, COW và di chuyển là một số điều đáng chú ý. Như Voo đã đề cập trong phần bình luận dưới đây, việc trả về theo giá trị thường dễ đọc hơn. Trong trường hợp của std::string và các chương trình cấp cao hơn, việc trả về theo giá trị không có khả năng là vấn đề, nhưng điều quan trọng là phải đo lường chi phí liên quan đến việc triển khai thư viện chuẩn mà bạn đang sử dụng nếu hiệu suất là quan trọng (câu hỏi của bạn có thể là trường hợp).

Điều quan trọng là nếu bạn đang cố gắng cải thiện chương trình hiện tại, hãy đảm bảo bạn hiểu cách triển khai thực hiện và tìm hiểu cách sử dụng các loại hiệu quả nhất khi hiệu suất là quan trọng. Việc triển khai có thể được viết và tối ưu hóa cho việc sử dụng thực tế sử dụng, có nghĩa là chúng có thể bi quan và đoán lần thứ hai bạn trong một số trường hợp và nỗ lực cải thiện hiệu suất của bạn có thể đã được triển khai hoặc sử dụng độc đáo loại có thể làm suy giảm hiệu suất. Hành vi thay đổi kích thước điển hình của một std::vector là một ví dụ rõ ràng. Việc sử dụng đường hiệu suất cao sẽ làm tăng thêm thời gian và sự phức tạp về những gì bạn cần biết để đạt được kết quả tốt nhất, và điều này rõ ràng khác nhau tùy theo cách bạn sử dụng và cả các loại bạn đang sử dụng. Nếu hiệu suất các khoản đầu tư thời gian quan trọng và đáng giá, việc tìm hiểu hoạt động của các loại bạn sử dụng là một nỗ lực đáng giá có thể dẫn đến lợi ích đáng kể.

Tôi cũng nên thêm rằng tôi làm việc ở mức độ thấp khá thường xuyên - Trường hợp hiệu suất là quan trọng và/hoặc tài nguyên bị giới hạn. Có thể có nhiều hạn chế, bao gồm không có ngoại lệ, không có khóa (cũng ngụ ý không phân bổ đống), chi phí trừu tượng tối thiểu và thậm chí hạn chế sử dụng đa hình động. Nó có thể được coi là một miền khá đòi hỏi, ngay cả đối với C++. Tôi chọn tham chiếu cho các phần cấp thấp cốt lõi, nhưng tôi sẽ thư giãn quy tắc đó nếu tôi biết một chương trình sẽ chỉ được sử dụng trong các miền cấp cao hơn hoặc các bài kiểm tra đơn vị.

+0

@anonymous_downvoter Điều đó không giúp được gì nhiều, trừ khi bạn biện minh cho hành động của mình. – justin

+1

Trong khi tôi thực sự nghĩ rằng việc trả lại đối tượng dẫn đến việc đọc mã đơn giản và dễ dàng hơn, bạn chắc chắn sẽ tạo ra một đối số hợp lý ở đây, vì vậy +1 - có thể loại bỏ ctor bản sao và toán tử = thực sự tốt đẹp và tôi rất thích để làm điều đó thường xuyên hơn .. cũng chẳng có gì hoàn hảo cả. – Voo

+0

@Voo Vâng, tôi đồng ý rằng việc trả về giá trị thường khá dễ đọc hơn (đặc biệt khi kiểu này đơn giản là 'std :: string'). Tôi sẽ thêm một vài chi tiết/nền cho câu trả lời này ngay bây giờ mà tôi đã đọc lại nó. Chúc mừng. – justin

1

Tôi thích phiên bản thứ hai tốt hơn, bởi vì nó trông giống như một hàm toán học. Nếu bạn chỉ trở về chuỗi bạn nên có hiệu suất tốt khôn ngoan.

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