2012-01-18 28 views
10

Tôi hiện đang đọc trong "The C++ Programming Language: Special Edition" của Bjarne Stroustrup và trên trang 133 nó khẳng định như sau:cải thiện tốc độ gợi ý khi xác định chuỗi có giá trị ngay lập tức, thay vì trì hoãn

Đối các loại do người dùng xác định, trì hoãn định nghĩa của biến cho đến khi bộ khởi tạo thích hợp có sẵn cũng có thể dẫn đến hiệu suất tốt hơn . Ví dụ:

string s; /* .... */ s = "The best is the enemy of the good."; 

có thể dễ dàng thể chậm hơn nhiều so với

string s = "Voltaire"; 

Tôi biết nó nói có thể dễ dàng, có nghĩa là nó sẽ không nhất thiết phải như vậy, tuy nhiên chúng ta hãy chỉ nói nó xảy ra.

Tại sao điều này làm tăng hiệu suất tiềm năng?

Chỉ có các loại do người dùng xác định (hoặc thậm chí loại STL) hay đây cũng là trường hợp với int, float, v.v ...?

+0

Bên cạnh các câu trả lời bên dưới, điều này có thể trợ giúp: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.22 – dsign

Trả lời

9

Tôi muốn nói điều này chủ yếu là về các loại với các nhà xây dựng mặc định không tầm thường, ít nhất là liên quan đến hiệu suất.

Sự khác biệt giữa hai cách tiếp cận là:

  • Trong phiên bản đầu tiên, một chuỗi rỗng là lần đầu tiên được xây dựng (sử dụng constructor mặc định); sau đó toán tử gán được sử dụng để loại bỏ hiệu quả công việc được thực hiện bởi hàm tạo mặc định và gán giá trị mới cho chuỗi.
  • Trong phiên bản thứ hai, giá trị yêu cầu được đặt ngay lập tức, tại thời điểm xây dựng.

Tất nhiên, thật khó để nói cho một ưu tiên về sự khác biệt lớn về hiệu suất này.

+0

Sự khác biệt hiệu suất là đáng chú ý đối với các loại do người dùng xác định. thực tế không có loại POD nào. –

7
  1. Phải mất thời gian để thực hiện một constructor mặc định. Ghi đè những gì nó đã khởi tạo chuỗi vào sau toán tử gán được gọi cũng mất thời gian.

  2. Việc thực hiện có thể không bao giờ đạt được nhiệm vụ, khi hàm (do tuyên bố return hoặc ngoại lệ) còn lại giữa các lời gọi của hàm tạo mặc định và toán tử gán. Trong trường hợp đó, đối tượng là được khởi tạo mặc định không cần thiết.

  3. Triển khai có thể lãng phí thực hiện để đảm bảo destructor của các đối tượng được gọi là nếu một ngoại lệ được ném. Nếu đối tượng được khởi tạo trong một phạm vi tiếp theo không bao giờ đạt được, điều đó cũng không cần thiết.

+1

Tôi nghĩ rằng vấn đề phạm vi là điều quan trọng nhất ở đây, và tất cả các câu trả lời khác đã né tránh nó. Điều không rõ ràng từ Stroustrup là trong trường hợp sau, bạn có thể tránh sử dụng biến hoàn toàn. –

1

Bởi vì:

string s; /* .... */ s = "The best is the enemy of the good.";  

Liên quan đến hai hoạt động: Xây dựng và Chuyển nhượng

Trong khi:

string s = "Voltaire"; 

Liên quan đến việc chỉ xây dựng.

Điều này tương đương với việc chọn Member Initializer lists over Assignment in Constructor body.

-1

Lớp có ba cách để khởi tạo chuỗi:

string s;   // Default constructor 
string s = "..."; // Default constructor followed by operator assignment 
string s("..."); // Constructor with parameters passed in 

Chuỗi lớp cần phải cấp phát bộ nhớ. Nó là tốt hơn để phân bổ nó một khi nó biết bao nhiêu bộ nhớ nó cần.

+2

Trường hợp thứ hai là khởi tạo bản sao và không bao giờ sử dụng hàm tạo mặc định hoặc toán tử gán. Đó là kỹ thuật xây dựng tạm thời thông qua chuyển đổi ctor và sau đó sao chép xây dựng 's', nhưng bản sao thừa thường elided, làm cho nó tương đương với initialisation trực tiếp (trường hợp 3). –

0

Đó là một câu hỏi hay. Bạn nói đúng, điều này chỉ xảy ra với các loại phức tạp. I E. các lớp và cấu trúc, std :: string là một đối tượng. Vấn đề thực sự liên quan ở đây là phải làm với constructor.

Khi một đối tượng được tạo ra, ví dụ:

std::string s; 

constructor Nó được gọi là, nó có thể phân bổ một số bộ nhớ, hiện một số khởi tạo biến khác, được bản thân đã sẵn sàng để sử dụng. Trong thực tế, một số lượng lớn mã có thể được thực hiện tại thời điểm này trong mã.

Sau đó bạn làm:

s = "hello world!"; 

Điều này làm cho lớp phải vứt bỏ hầu hết những gì nó được thực hiện và chuẩn bị sẵn sàng để thay thế nội dung của nó với một chuỗi mới.

này là thực sự giảm xuống còn một thao tác đơn giản nếu bạn thiết lập giá trị khi biến được định nghĩa, ví dụ:

std::string s = "Hello world"; 

sẽ trên thực tế, nếu bạn xem mã trong một trình gỡ lỗi, hãy thực hiện một constructor khác nhau một lần thay vì xây dựng đối tượng và sau đó, riêng biệt, đặt giá trị. Trên thực tế, mã trước hoạt động giống như:

std::string s("Hello world"); 

Tôi hy vọng điều này sẽ giúp mọi thứ rõ ràng hơn một chút.

+1

Trên thực tế, hàm tạo mặc định 'chuỗi' của một triển khai tốt có thể không nhất thiết cấp phát bộ nhớ. Tôi biết chắc chắn rằng 'vector' của gcc thì không. –

+1

Chi phí thực tế có lẽ là một thực tế rằng nhiệm vụ là phức tạp hơn xây dựng, bởi vì nó đã xem xét xử lý và/hoặc tái sử dụng trạng thái trước đó của chuỗi. Hàm khởi tạo biết rằng không có trạng thái trước đó. –

0

Hãy xem xét điều gì xảy ra trong cả hai trường hợp.Trong trường hợp thứ nhất: constructor

  • mặc định kêu gọi "s" hành
  • phân kêu gọi "s"

Trong trường hợp thứ hai, lần đầu tiên xem xét điều đó với bản sao sự bỏ bớt này tương đương với string s("Voltaire") , như sau:

  • c-string constructor gọi

Về mặt logic, cách tiếp cận đầu tiên yêu cầu máy trừu tượng để làm việc nhiều hơn. Cho dù điều này thực sự dịch sang mã thực hơn phụ thuộc vào loại thực tế và bao nhiêu trình tối ưu hóa có thể thực hiện. Mặc dù lưu ý rằng đối với tất cả các kiểu người dùng tầm thường, trình tối ưu hóa có thể phải giả định hàm tạo mặc định có các tác dụng phụ, do đó không thể đơn giản loại bỏ nó.

Chi phí bổ sung này chỉ áp dụng cho các loại người dùng vì chi phí nằm trong hàm tạo mặc định. Đối với bất kỳ kiểu nguyên thủy nào như int, hoặc trên thực tế bất kỳ với một hàm tạo/bản sao tầm thường, không có chi phí cho hàm tạo mặc định - dữ liệu đơn giản sẽ không được khởi tạo (khi ở trong phạm vi hàm).

0

Tại sao điều này làm tăng hiệu suất tiềm năng?

Trường hợp đầu tiên liên quan đến việc khởi tạo mặc định, tiếp theo là chuyển nhượng; thứ hai liên quan đến việc khởi tạo từ giá trị. Khởi tạo mặc định có thể làm một số công việc mà sau này phải được làm lại (hoặc thậm chí hoàn tác) bằng cách gán, và vì vậy trường hợp đầu tiên có thể liên quan đến nhiều công việc hơn thứ hai.

Chỉ có các loại do người dùng xác định (hoặc thậm chí loại STL) hay đây cũng là trường hợp với int, float, v.v ...?

Chỉ với các loại do người dùng xác định; và sau đó nó phụ thuộc vào những gì các nhà xây dựng và nhà điều hành gán thực sự làm. Đối với các loại vô hướng, khởi tạo mặc định không làm gì cả, và phép gán cũng thực hiện tương tự như khởi tạo từ một giá trị, vì vậy cả hai lựa chọn thay thế sẽ là tương đương.

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