2009-10-09 33 views
31

Tôi undersatnd rằng tài liệu tham khảo không phải là con trỏ, nhưng một bí danh cho một đối tượng. Tuy nhiên, tôi vẫn không hiểu chính xác điều này có ý nghĩa gì đối với tôi với tư cách là một lập trình viên, tức là tài liệu tham khảo dưới mui xe là gì?Tại sao tôi không thể lưu trữ các tham chiếu trong bản đồ STL trong C++?

Tôi nghĩ cách tốt nhất để hiểu điều này là hiểu tại sao tôi không thể lưu trữ tham chiếu trong bản đồ.

Tôi biết tôi cần phải dừng lại suy nghĩ của tài liệu tham khảo như suger cú pháp trên con trỏ, chỉ cần không chắc chắn làm thế nào để:/

+4

Đừng nghĩ đến các tham chiếu dưới dạng con trỏ bị hạn chế. Hãy coi chúng là bí danh đối với các đối tượng hiện tại không được phép để lủng lẳng. – sbi

Trả lời

23

Họ cách tôi hiểu nó, tài liệu tham khảo được thực hiện như gợi ý dưới mui xe. Lý do tại sao bạn không thể lưu trữ chúng trong một bản đồ hoàn toàn là ngữ nghĩa; bạn phải khởi tạo một tham chiếu khi nó được tạo và bạn không thể thay đổi nó sau đó nữa. Điều này không phù hợp với cách bản đồ hoạt động.

+0

Bạn có nghĩ rằng tham chiếu chiếm bất kỳ "bộ nhớ" nào không? Tôi biết con trỏ làm. – Jaywalker

+0

@Jaywalker: đúng vậy. Trong mã assembly được tạo, chúng được thực hiện như con trỏ. –

+3

Nó rõ ràng không được chỉ định trong tiêu chuẩn cho dù chúng có lưu trữ hay không. Thông thường họ làm, nhưng đôi khi nó có thể được tối ưu hóa đi. – sp2danny

7

Sự khác biệt quan trọng ngoài các đường cú pháp là tài liệu tham khảo không thể thay đổi để đề cập đến một đối tượng hơn cái mà chúng đã được khởi tạo. Đây là lý do tại sao chúng không thể được lưu trữ trong bản đồ hoặc các vùng chứa khác, bởi vì các thùng chứa cần phải có khả năng sửa đổi kiểu phần tử chúng chứa.

Như một minh hoạ điều này:

A anObject, anotherObject; 
A *pointerToA=&anObject; 
A &referenceToA=anObject; 

// We can change pointerToA so that it points to a different object 
pointerToA=&anotherObject; 

// But it is not possible to change what referenceToA points to. 
// The following code might look as if it does this... but in fact, 
// it assigns anotherObject to whatever referenceToA is referring to. 
referenceToA=anotherObject; 
// Has the same effect as 
// anObject=anotherObject; 
+2

Điều này nghe có vẻ tốt với tôi, ngoại trừ "các container cần phải có khả năng sửa đổi kiểu phần tử mà chúng chứa." Điều này làm cho nó âm thanh như bản đồ cần để có thể thay đổi kiểu dữ liệu nó lưu trữ, trong khi thực sự nó chỉ cần để có thể thay đổi "yếu tố" chính nó. – bambams

21

Bạn nên suy nghĩ của một tham chiếu như là một 'con trỏ const đến một đối tượng không const':

MyObject& ~~ MyObject * const 

Bên cạnh đó, một tham chiếu chỉ có thể được xây dựng như một bí danh của một cái gì đó mà tồn tại (đó là không cần thiết cho một con trỏ, mặc dù được khuyên dùng ngoài NULL). Điều này không đảm bảo rằng đối tượng sẽ ở xung quanh (và thực sự bạn có thể có một lõi khi truy cập vào một đối tượng thông qua một tài liệu tham khảo nếu nó không còn nữa), hãy xem xét mã này:

// Falsifying a reference 
MyObject& firstProblem = *((MyObject*)0); 
firstProblem.do(); // undefined behavior 

// Referencing something that exists no more 
MyObject* anObject = new MyObject; 
MyObject& secondProblem = *anObject; 
delete anObject; 
secondProblem.do(); // undefined behavior 

Giờ đây, có hai yêu cầu cho một container STL:

  • T phải constructible mặc định (một tham chiếu không phải là)
  • T phải chuyển nhượng (bạn không thể thiết lập lại một tài liệu tham khảo, mặc dù bạn có thể gán cho trọng tài của nó)

Vì vậy, trong các container STL, bạn phải sử dụng proxys hoặc con trỏ.

Bây giờ, sử dụng con trỏ có thể chứng minh vấn đề đối với việc xử lý bộ nhớ, vì vậy bạn có thể phải:

con trỏ thông minh

KHÔNG sử dụng auto_ptr, có sự cố với chuyển nhượng kể từ khi nó sửa đổi toán hạng tay phải.

Hy vọng nó sẽ giúp :)

+3

Điểm nhỏ: 'MyObject & firstProblem = * ((MyObject *) 0);' là hành vi chưa được xác định (bạn không initalizing tham chiếu với đối tượng hợp lệ) và do đó điều này một mình, theo tiêu chuẩn C++, được phép định dạng đĩa cứng của bạn hoặc làm những điều xấu khác cho bạn. (Ma quỷ, ai không?) Nhưng đó chỉ là một điểm rất nhỏ và nếu không thì bạn là một câu trả lời tuyệt vời. +1 – sbi

+1

Một điểm nhỏ khác: có vẻ như với tôi rằng khả năng xây dựng mặc định không phải là một yêu cầu (ngoại trừ một số phương thức sử dụng cá thể được tạo mặc định cho đối số mặc định). (20.1.4.1) Tôi nghĩ rằng bạn có nghĩa là * sao chép * -constructible. – UncleBens

+0

Chính xác, đối với container STL nói chung không cần thiết (khái niệm). Đối với một bản đồ, không sử dụng giá trị mặc định Constructible sẽ làm mất hiệu lực sử dụng toán tử thuê bao (-> operator []) mặc dù ... và cá nhân tôi nhận được thông báo lỗi trong VC++ 2008 nếu tôi cố gắng sử dụng kiểu không được cấu hình mặc định: "lỗi C2512: 'Đặc biệt :: Đặc biệt': không có hàm tạo mặc định thích hợp nào có sẵn" –

0

bài này giải thích cách con trỏ được thực hiện dưới mui xe - http://www.codeproject.com/KB/cpp/References_in_c__.aspx, mà còn hỗ trợ Sebastians trả lời.

+1

Có một vài lỗi với tài liệu được tham chiếu. Đối với một tài liệu tham khảo không nhất thiết phải liên tục con trỏ (họ có thể được thực hiện theo cách đó). Xem nhận xét của jefito ở dưới cùng. – Taras

1

Vùng chứa lưu trữ một tham chiếu để khởi tạo tất cả các phần tử của nó khi được tạo và do đó ít hữu ích hơn.

struct container 
{ 
    string& s_;   // string reference 
}; 

int main() 
{ 
    string s { "hello" }; 
    //container {};  // error - object has an uninitialized reference member 
    container c { s }; // Ok 
    c.s_ = "bye"; 
    cout << s;   // prints bye 
} 

Ngoài ra, khi được khởi tạo, không thể thay đổi bộ nhớ cho các thành phần vùng chứa. s_ sẽ luôn luôn tham chiếu đến lưu trữ của ở trên.

1

thực sự bạn có thể sử dụng các tham chiếu trong bản đồ. tôi không khuyên bạn nên làm điều này cho các dự án lớn vì nó có thể gây ra lỗi biên dịch lạ nhưng:

map<int, int&> no_prob; 
    int refered = 666; 
    no_prob.insert(std::pair<int, int&>(0, refered)); // works 
    no_prob[5] = 777; //wont compile!!! 
    //builds default for 5 then assings which is a problem 
    std::cout << no_prob[0] << std::endl; //still a problem 
    std::cout << no_prob.at(0) << std::endl; //works!! 

vì vậy bạn có thể sử dụng bản đồ (thường cạnh tranh) mã số

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