2009-12-25 74 views
5
class mystring { 
friend ostream& operator<<(ostream &out, const mystring ss) { 
     out << ss.s; 
     return out; 
    } 
private: 
    string s; 
public: 
    mystring(const char ss[]) { 
     cout << "constructing mystring : " << ss << endl; 
     s = ss; 
    } 
}; 

void outputStringByRef(const mystring &ss) { 
cout << "outputString(const string&) " << ss << endl; 
} 

void outputStringByVal(const mystring ss) { 
cout << "outputString(const string) " << ss << endl; 
} 

int main(void) { 
    outputStringByRef("string by reference"); 
    outputStringByVal("string by value"); 
    outputStringByRef(mystring("string by reference explict call mystring consructor")); 
    outputStringByVal(mystring("string by value explict call mystring constructor")); 
} ///:~ 

Xem xét ví dụ trên, chúng tôi không thể sửa đổi biến vượt qua tham chiếu, chúng tôi cũng không thể sửa đổi biến giá trị theo giá trị. Đầu ra của mỗi phương thức giống nhau. Vì vậy, không có sự khác biệt giữa hai phương pháp này phương pháp, tại sao C + + hỗ trợ cả hai phương pháp?Bất kỳ sự khác biệt nào giữa f (const string &) và f (const string)?

cảm ơn.

+2

http://stackoverflow.com/questions/1567138/const-t-arg-vs-t-arg/1567186#1567186 –

Trả lời

19

Có sự khác biệt giữa hai loại. Hãy xem xét những điều sau đây:

#include <iostream> 
#include <string> 
using std::string; 

string g_value; 

void callback() { 
    g_value = "blue"; 
} 

void ProcessStringByRef(const string &s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

void ProcessStringByValue(const string s) { 
    callback(); 
    std::cout << s << "\n"; 
} 

int main() { 
    g_value = "red"; 
    ProcessStringByValue(g_value); 
    g_value = "red"; 
    ProcessStringByRef(g_value); 
} 

Output:

red 
blue 

Chỉ vì một tham chiếu là const bên trong một hàm, không có nghĩa rằng referand không thể được sửa đổi thông qua tài liệu tham khảo khác (tình hình của một đối tượng có nhiều tham chiếu hoặc con trỏ đến nó được gọi là "răng cưa"). Do đó có một sự khác biệt giữa việc truyền tham chiếu const và truyền một giá trị const - trong trường hợp tham chiếu, đối tượng có thể thay đổi sau khi thực hiện cuộc gọi. Trong trường hợp của giá trị, các callee có một bản sao riêng tư, mà sẽ không thay đổi.

Vì chúng làm những việc khác nhau, C++ cho phép bạn chọn thứ bạn muốn.

Có những hậu quả về hiệu suất theo một trong hai cách - khi bạn vượt qua theo giá trị, bản sao phải được thực hiện, chi phí nào. Nhưng trình biên dịch sau đó biết rằng chỉ có chức năng của bạn có thể có thể có bất kỳ tham chiếu đến bản sao đó, mà có thể cho phép tối ưu hóa khác. ProcessStringByRef không thể tải nội dung của chuỗi để in cho đến khi callback() đã trở lại. ProcessStringByValue có thể, nếu trình biên dịch nghĩ rằng làm như vậy là nhanh hơn.

Thông thường bạn quan tâm đến bản sao, không phải là thứ tự thực hiện các hướng dẫn, vì thường là bản sao đắt hơn. Vì vậy, thông thường, bạn chuyển qua tham chiếu nếu có thể cho các đối tượng không tầm thường để sao chép. Nhưng khả năng tạo răng cưa đôi khi có hậu quả thực sự nghiêm trọng đối với hiệu suất, bằng cách ngăn chặn tối ưu hóa nhất định mặc dù không có răng cưa thực sự xảy ra. Đó là lý do tại sao "quy tắc bí danh nghiêm ngặt" tồn tại và từ khóa restrict trong C99.

1

Với outputStringByRef bạn phải đảm bảo rằng biến bạn đang chuyển một tham chiếu đến vẫn còn sống và không thay đổi miễn là outputStringByRef cần đến. với outputStringByVal biến bạn vượt qua có thể chết hoặc thoát khỏi phạm vi và bản sao mà hàm vẫn còn ok.

8

f(const string&) lấy chuỗi bằng const tham chiếu: f đang hoạt động trực tiếp trên đối tượng chuỗi được truyền theo tham chiếu: không có bản sao nào liên quan. const ngăn không cho sửa đổi đối tượng gốc.

f(const string) lấy giá trị chuỗi, có nghĩa là f được cấp một bản sao của chuỗi gốc. Ngay cả khi bạn thả const, khi truyền theo giá trị, bất kỳ sửa đổi nào đối với chuỗi bị mất khi trả về f.

Tôi không biết chính xác những gì bạn muốn nói "tại sao C++ hỗ trợ cả hai phương pháp?". Nó chỉ là quy tắc quá tải chung áp dụng.

+0

'tại sao C++ hỗ trợ cả hai phương thức', vì lúc đầu, tôi không thấy sự khác biệt giữa hai function.i này nghĩ rằng hai là giống nhau. Sau khi đào vào g + lắp ráp và msvc tạo ra, có vẻ như nhiều cách tương tự bằng cách đi qua cùng một địa chỉ của hằng số. – Jichao

3

đối tượng của bạn có thể chứa một thành viên mutable, có thể được sửa đổi ngay cả với một tham chiếu const

6

f(string s) đi qua giá trị chuỗi s, nói cách khác nó tạo ra một bản sao và khởi tạo nó với giá trị của chuỗi bạn vượt qua. Bất kỳ thay đổi nào đối với bản sao, sẽ không được truyền sang chuỗi gốc mà bạn đã chuyển để gọi hàm. Trong f(const string s) const là thừa vì bạn không thể thay đổi giá trị ban đầu.

Trong f(const string& s) thay vì chuỗi không được sao chép nhưng bạn chuyển một tham chiếu đến chuỗi đó. Điều này thường được thực hiện khi bạn có một đối tượng lớn để "pass-by-value" có thể tạo ra một overhead (đó là lý do tại sao C++ hỗ trợ cả hai phương thức). Đi qua tham chiếu có nghĩa là bạn có thể thay đổi giá trị của đối tượng "lớn" mà bạn vượt qua, nhưng vì trình chỉ định const, bạn không thể sửa đổi nó. Đó là một loại "bảo vệ".

+1

'const chuỗi s' có thể là cần thiết nếu nó tốt hơn chuyển tải ý định – philsquared

+0

Sự khác biệt trong ý định giữa' chuỗi s' và 'const chuỗi s' là gì? –

+0

Martin B: đó là một chi tiết bên trong cho hàm và có thể giúp ai đó phải viết hàm đó, nhưng chữ ký tương thích (bạn có thể làm 'void f (int const); typedef void (* FP) (int); FP p = & f; ') và nó không có ý nghĩa đối với người gọi. –

1

Có (hầu như) không có sự khác biệt về việc có thể sửa đổi chuỗi trong hàm. Tuy nhiên có một sự khác biệt lớn về những gì đang được thông qua. Quá tải const mystring &ss mất tham chiếu const vào chuỗi. Mặc dù nó không thể sửa đổi nó, nó là cùng một bộ nhớ được giải quyết. Nếu chuỗi dài này có thể là một yếu tố lớn (giả sử chuỗi không được thực hiện bằng cách sử dụng copy-on-write). Biểu mẫu const mystring ss đang tạo một bản sao của chuỗi, vì vậy bộ nhớ khác nhau sẽ được giải quyết.

Thực tế, hình thức const mystring &sscó thể thay đổi chuỗi, nếu sử dụng const_cast<mystring&> - mặc dù tôi sẽ không đề xuất ở đây.

1

Hãy tưởng tượng bạn muốn in một đối tượng mà không thể được sao chép:

Thread th; 
// ... 
cout << th; // print out informations... 

Phiên bản tài liệu tham khảo sẽ không sao chép các chủ đề, nhưng sẽ mất địa chỉ của th và tạo một bí danh đến nó.Các phiên bản khác sẽ cố gắng sao chép các chủ đề khi đi qua giá trị - nhưng sao chép có thể không được ý nghĩa đối với một đối tượng như vậy (sẽ có thêm một chủ đề sau đó?).

Điều này có thể giúp bạn nghĩ về việc sao chép đối tượng làm sao chép đối tượng. Sao chép th ở trên trong C++ không chỉ đơn thuần có nghĩa là có một xử lý khác đối với cùng một đối tượng như trong Java - nó có nghĩa là ngầm sao chép đối tượng được biểu thị và có toàn bộ bản sao của nó. Vì vậy, câu hỏi tương tự như "Tại sao Java hỗ trợ cả hai Object.clone và sao chép tài liệu tham khảo?" - cả hai đều có mục đích khác nhau.

Và sau đó cũng có vấn đề về hiệu suất. Bạn không muốn sao chép mỗi khi bạn vượt qua một cái gì đó xung quanh cho các đối tượng đói tài nguyên.

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