2011-02-16 27 views
7

Possible Duplicate:
FAQ: How to pass objects to functions in C++?
Pointer vs. ReferenceGọi bằng cách tham khảo vs luận Pointer

Hi all,
trong c/C++, chúng ta có thể vượt qua một đối tượng như cuộc gọi bằng cách tham chiếu hoặc đi qua con trỏ của đối tượng.
ví dụ:
tôi muốn tạo một hàm sẽ lấy chuỗi chuỗi làm đầu vào và xuất bản đồ chứa một số giá trị cho mỗi chuỗi. giá trị trả về của hàm là bool, cho biết thành công hay thất bại.

chức năng (gọi bằng cách tham khảo)

 
bool calculateSomeValue(vector<string> input, map<string, double>& result) 
{ 
//// bla bla bla 
return true/false; 
} 

chức năng (sử dụng con trỏ)

 
bool calculateSomeValue(vector<string> input, map<string, double>* result) 
{ 
//// bla bla bla 
return true/false; 
} 

nào là tốt nhất? không ai có bất kỳ ý tưởng về những ưu và khuyết điểm của hai lựa chọn này?

cảm ơn trước.

+4

Con trỏ đến tham chiếu? Thật thú vị ... @ user619237: Thực hiện tìm kiếm 'con trỏ so với tham chiếu' trên StackOverflow và bạn sẽ nhận được nhiều câu trả lời hơn bao giờ hết. :] – Mehrdad

+2

http://stackoverflow.com/questions/114180/pointer-vs-reference – sinek

+2

Bạn thậm chí không cần phải tìm kiếm, chỉ cần nhìn vào bên phải ở phần "Liên quan" :) – sinek

Trả lời

11

Đây là vấn đề về phong cách.Tại Google (xem Google C++ style guidelines), sau đây sẽ được ưa thích:

bool CalculateSomeValue(
    const vector<string>& input, map<string, double>* result); 

Điều này là do sử dụng một con trỏ đòi hỏi việc sử dụng rõ ràng của một dấu tại địa điểm cuộc gọi:

CalculateSomeValue(input, &result); 

Trái ngược với đường đi nó sẽ được gọi với một loại tham chiếu:

CalculateSomeValue(input, result); 

Bằng cách bắt buộc sử dụng dấu và trong trường hợp thông số được sửa đổi, rõ ràng tại trang web gọi appen. Khi sử dụng tài liệu tham khảo, nó sẽ trở nên cần thiết để tra cứu tài liệu cho mỗi và mọi chức năng để biết liệu nó có tiềm năng sửa đổi các tham số đầu vào của nó hay không.

Tuy nhiên, việc sử dụng con trỏ cũng có những nhược điểm của nó. Đặc biệt, việc sử dụng con trỏ có nghĩa là bạn thay đổi trách nhiệm xử lý null từ người gọi, nơi con trỏ sẽ bị bỏ qua (nếu biến là con trỏ và không chỉ là biểu thức địa chỉ với biến cục bộ) cho hàm. Tức là, khi sử dụng loại con trỏ, CalculateSomeValue cần kiểm tra nullptr (hoặc cần ghi rõ tài liệu yêu cầu kiểm tra này trong người gọi), trong khi việc sử dụng loại tham chiếu là tự ghi lại tài liệu và chỉ rõ rằng không có giá trị tham chiếu được mong đợi.

Đối situtation đặc biệt này, cá nhân tôi đánh giá cao đề nghị một phương pháp lai:

bool CalculateSomeValue(
    const std::vector<std::string>& input, 
    Output<map<string, double>> result); 

... nơi Output<T> được tạo ra bởi một hàm với chữ ký:

template<typename T> Output<T> WriteTo(T& output_ref); 

... và ở đâu Các nhà khai thác quá tải Output<T>->, *, v.v. Điều này về cơ bản buộc các trang web cuộc gọi phải rõ ràng trong thực tế rằng đầu vào sẽ bị đột biến bằng cách yêu cầu:

CalculateSomeValue(input, WriteTo(result)); 

... như trái ngược với:

CalculateSomeValue(input, result); 

... trong khi đồng thời đạt được ngữ nghĩa không null/cú pháp của tài liệu tham khảo.

+1

Một ngoại lệ đáng chú ý IMHO: tham chiếu const được ưu tiên hơn con trỏ const. – Mehrdad

+0

Có. OP đã hỏi về kết quả đầu ra (hoặc ít nhất đó là cách tôi giải thích câu hỏi). Bạn sẽ nhận thấy trong câu trả lời của tôi rằng tôi sử dụng tham chiếu const cho tham số đầu vào. –

+0

@Michael: Rất tiếc, lỗi của tôi - tôi đã bỏ lỡ cụm từ "nơi các thông số được ** sửa đổi **". – Mehrdad

0
bool calculateSomeValue(vector<string> input, map<string, double>&* result) 

Con trỏ tham chiếu? Điều này thậm chí sẽ không biên dịch. Đầu tiên là chính xác và chỉ chính xác có thể là tốt nhất!

struct A {}; 

void f(A & *a) {} 

Compile cho rrror:

prog.cpp:7: error: cannot declare pointer to ‘struct A&’

Ideone mẫu: http://www.ideone.com/8PJA5

0

mẫu mã thứ hai của bạn có vẻ là không chính xác, như bạn đang cố gắng để chấp nhận một con trỏ đến một tài liệu tham khảo, trong đó shouldn không biên dịch. Bằng cách tham khảo và truyền con trỏ cũng hiệu quả (ai đó sửa tôi nếu tôi sai, nhưng tôi hiểu rằng tham chiếu về cơ bản là con trỏ được tạo tự động sau đó được tham chiếu liền mạch), nhưng đi qua tham chiếu được khuyến nghị vì nó an toàn hơn:

  • Bạn không thể có tham chiếu null.
  • Bạn không thể thay đổi địa chỉ được tham chiếu, trong khi con trỏ sẽ cho phép bạn.

Một số người thích cú pháp con trỏ rõ ràng hơn, làm cho nó rõ ràng rằng đó là một con trỏ tới một đối tượng được truyền vào (như bạn phải dereference).

+0

@Andrew: Tôi từng nói câu "bạn không thể có một tham chiếu null" và gần như bị đánh đến chết vì nó, vì '(int &) (* (int *) NULL)' về mặt kỹ thuật là một tham chiếu null. Tôi đồng ý với những gì bạn nói, nhưng chỉ cảnh báo bạn rằng mọi người có thể lo sợ về câu đó. :) – Mehrdad

+0

@Mehrdad: Đó là một vặn lại sai, vì dereferencing một con trỏ null dẫn đến hành vi không xác định. Nói cách khác, nếu tôi có một hàm 'void f (int & i)', tôi biết rằng 'i' sẽ luôn luôn tham chiếu một' int'. Nếu bạn nói, "' i' có thể không được, vượt qua '* ((int *) 0)' "(nhân tiện, đoạn cuối cùng để' int & 'thừa), sau đó bạn đã đánh bại bản thân vì bạn không thể giả định bất kỳ hành vi chương trình nào sau khi biểu thức đó được đánh giá (bạn không thể truy cập 'i' bên trong' f' bởi vì "gọi hàm" là vô nghĩa). Nói cách khác, vì UB, thuật ngữ "tham chiếu" là vô nghĩa, và không thể nói là rỗng. – GManNickG

+0

Hoặc như một sự tương tự, đó là giống như tôi nói "Đeo dây an toàn cứu sống" và bạn vặn lại "Không nếu bạn không mặc chúng!". Nếu bạn không mặc nó, tuyên bố không thể áp dụng. – GManNickG

0

Giải pháp tốt nhất không tồn tại. Nó đi từ trường hợp này sang vụ khác.

Ví dụ, tiếp theo có thể được tốt hơn trong một số trường hợp:

map<string, double> calculateSomeValue(const vector<string> &input) 
{ 
    map<string, double> res; 
// do something with input to form the output 
    return res; 
} 

Nhưng, nếu bạn có thể, thích sử dụng các thuật toán tiêu chuẩn

+0

Giá trị trả về thường là một cách tốt để đi, nhưng trong ví dụ OP, kiểu trả về là void và không phải là bool, vì vậy bạn vẫn sẽ cần phải có cách để trả về giá trị khác đó. –

+0

@Michael Trong ví dụ OPs, các giá trị trả về là kiểu bool, và tôi không chắc tôi hiểu nhận xét của bạn. Bạn có thể làm rõ? –

+1

Tôi nói rằng việc sử dụng giá trị trả về cho kết quả (thay vì truyền trong một tham chiếu hoặc con trỏ vào đó để viết kết quả) là một phương án tương đương khi kiểu trả về trong trường hợp con trỏ/tham chiếu đã bị vô hiệu. Trong trường hợp này, khi giá trị trả về đã được sử dụng cho một thứ khác, một giá trị bắt buộc phải sử dụng các tham số đầu ra hoặc để tạo một đối tượng đóng gói chứa tất cả thông tin được trả về. –

0

Tôi giả định thứ hai được coi là chỉ bằng cách điểm và không phải là con trỏ đến tham chiếu.

Quyết định giữa hai chỉ là vấn đề về phong cách và hương vị. Kết quả được biên dịch của cả hai dòng có thể giống nhau.

Thông thường cho các tham số đầu ra, hướng dẫn kiểu C++ cho biết nó phải bằng con trỏ.

0

Thời gian duy nhất để sử dụng tài liệu tham khảo là khi triển khai toán tử chuẩn yêu cầu chúng.

Tuy nhiên, bên ngoài đó, con trỏ phải luôn được ưu tiên hơn tham chiếu cho bất kỳ phương pháp ứng dụng nào.

  • Đối tượng người dùng thường được lưu trữ được lưu trữ trong con trỏ, thông minh hoặc cách khác. Các ràng buộc về tham chiếu để luôn hợp lệ chỉ là quá khắt khe khi các đối tượng đang được tạo theo yêu cầu. Điều này làm cho tham số tham chiếu bất tiện khi sử dụng vì một tham số cần thực hiện thao tác vụng về để truyền giá trị. referenceFunc((*pObj));

  • Đi qua tham chiếu là một thay thế rẻ tiền để cố triển khai mô hình hợp đồng. C++ nào không hỗ trợ.

  • Tất cả hoạt động (* somePtr) trên tham chiếu đó sẽ ngụ ý có nghĩa là bạn sẽ nhận con trỏ rỗng để gỡ lỗi, chỉ bị che khuất hơn vì con trỏ rỗng sẽ không giống con trỏ.

  • Không bao giờ giải quyết vấn đề bảo trì khi người lập trình không thể biết bằng cách kiểm tra đơn giản thông số nào cho hàm có thể thay đổi.

1

nếu đối số không phải là tùy chọn, tôi tìm thấy C++ nhiều nhà phát triển muốn chuyển qua tham chiếu. tuy nhiên, nhiều người chỉ muốn truyền con trỏ chỉ cho dấu hiệu trực quan. đây là một chút của một holdover từ c mà erodes như chương trình phát triển để đối phó với việc sử dụng hỗn hợp của tài liệu tham khảo và con trỏ.

bạn có thể giả định tham chiếu không phải là rỗng, nhưng bạn không nên giả định rằng đối với con trỏ. trong trường hợp đó, bạn sẽ phải giới thiệu giàn giáo/kiểm tra điều kiện tiên quyết để đảm bảo ai đó ngược dòng không tin rằng đối số là tùy chọn - nếu bạn phòng thủ.

cá nhân, tôi thích chuyển qua tham chiếu và sử dụng tên mô tả/hàm/biến mô tả (nếu bạn chỉ chi tiết nhãn, thì mọi người phải truy cập tài liệu thường xuyên hơn). điều này giữ cho ý định của chương trình rõ ràng và tránh được các kiểm tra bổ sung bằng văn bản. get* hoặc update* có thể rõ ràng hơn calculate*.

sử dụng tham chiếu là nhất quán, được xác định rõ và tạo ra các chương trình đơn giản hơn vì bạn không xử lý các loại biến thể/đối số hỗn hợp.

nó thực sự không tạo ra sự khác biệt đáng kể mà bạn chọn trong trường hợp này, chỉ cần là nhất quán khi bạn sử dụng. một cue khác tôi sử dụng là đặt các tham số đã sửa đổi vào cùng một vị trí. cụ thể, chúng đứng trước các đối số liên tục.

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