2012-06-11 29 views
7

Tôi đang học Go, và tôi hơi bối rối khi sử dụng con trỏ. Cụ thể, khi trả về một struct từ một hàm, khi nào nó thích hợp để trả về cá thể struct, và khi nào nó thích hợp để trả về một con trỏ tới cấu trúc?Khi nào thì một ý tưởng hay là trả về một con trỏ cho một cấu trúc?

Ví dụ mã:

type Car struct { 
    make string 
    model string 
} 

func Whatever() { 
    var car Car 

    car := Car{"honda", "civic"} 

    // ... 

    return car 
} 

các tình huống mà tôi muốn trả về một con trỏ là gì, và ở đâu tôi sẽ không muốn? Có một quy tắc tốt của ngón tay cái?

+4

Đây không phải là C ... –

+0

Quy tắc tương tự sẽ không áp dụng? – Carson

+2

không, các quy tắc khác nhau cho các ngôn ngữ khác nhau. Mọi ngôn ngữ đều có sự cẩn trọng, và cá nhân tôi không biết Go, vì vậy tôi không thể nói được, nhưng tôi biết rằng trong C, việc trả về một con trỏ tới một đối tượng được phân bổ trên stack là một không-không lớn. –

Trả lời

12

Có hai điều bạn muốn giữ lại trong tâm trí, hiệu suất và API.

Ô tô được sử dụng như thế nào? Nó là một đối tượng có trạng thái? Nó là một cấu trúc lớn? Thật không may, nó là không thể trả lời khi tôi không có ý tưởng những gì một chiếc xe. Thành thật mà nói, cách tốt nhất là xem những gì người khác làm và sao chép chúng. Cuối cùng, bạn sẽ có cảm giác về điều này. Bây giờ tôi sẽ mô tả ba ví dụ từ thư viện chuẩn và giải thích lý do tại sao tôi nghĩ rằng họ đã sử dụng những gì họ đã làm.

  1. hash/crc32: Chức năng crc32.NewIEEE() trả về một kiểu con trỏ (trên thực tế, một giao diện, nhưng loại cơ bản là một con trỏ). Một thể hiện của hàm băm có trạng thái. Khi bạn viết thông tin vào một băm, nó tổng hợp dữ liệu để khi bạn gọi phương thức Sum(), nó sẽ cho bạn trạng thái của một cá thể đó.

  2. time: Chức năng time.Date trả về cấu trúc Time. Tại sao? Thời gian là thời gian. Nó không có trạng thái. Nó giống như một số nguyên nơi bạn có thể so sánh chúng, toán học tiền định trên chúng, vv Nhà thiết kế API đã quyết định rằng một sửa đổi trong một thời gian sẽ không thay đổi cái hiện tại mà tạo một cái mới. Là một người sử dụng thư viện, nếu tôi muốn thời gian một tháng kể từ bây giờ, tôi sẽ muốn có một đối tượng thời gian mới, chứ không phải thay đổi đối tượng hiện tại của tôi. Thời gian cũng chỉ dài 3 từ. Nói cách khác, nó là nhỏ và sẽ không đạt được hiệu suất trong việc sử dụng một con trỏ.

  3. math/big: big.NewInt() là một điều thú vị. Chúng tôi hoàn toàn có thể đồng ý rằng khi bạn sửa đổi một số big.Int, bạn sẽ thường muốn một cái mới. A big.Int không có trạng thái bên trong, vậy tại sao nó lại là một con trỏ? Câu trả lời chỉ đơn giản là hiệu suất. Các lập trình viên nhận ra rằng ints lớn là… lớn. Thường xuyên phân bổ mỗi khi bạn thực hiện một phép toán có thể không thực tế. Vì vậy, họ quyết định sử dụng con trỏ và cho phép lập trình viên quyết định thời điểm phân bổ không gian mới.

Tôi đã trả lời câu hỏi của bạn chưa? Chắc là không. Đây là một quyết định thiết kế và bạn cần phải tìm ra nó theo từng trường hợp. Tôi sử dụng thư viện chuẩn làm hướng dẫn khi tôi thiết kế thư viện của riêng mình. Nó thực sự tất cả đi xuống để phán xét và làm thế nào bạn mong đợi mã khách hàng sử dụng các loại của bạn.

+1

tôi phải đọc một vài lần để nhận ra đây là một câu trả lời thực sự tốt. cảm ơn bạn. – Carson

+0

Câu trả lời hay. Tôi mới đến Go, và được giới thiệu ở đây bởi Stephen từ # go-nut trên IRC. –

1

Rất losely, trường hợp ngoại lệ có thể sẽ xuất hiện trong những hoàn cảnh cụ thể:

  • Return một giá trị khi nó thực sự nhỏ (không quá vài từ).
  • Trả về con trỏ khi chi phí sao chép sẽ ảnh hưởng đáng kể đến hiệu suất (kích thước là nhiều từ).
2

Thông thường, khi bạn muốn bắt chước kiểu hướng đối tượng, nơi bạn có "đối tượng" lưu trữ trạng thái và "phương pháp" có thể thay đổi đối tượng, thì bạn sẽ có hàm "hàm tạo" một con trỏ đến một cấu trúc (nghĩ về nó như là "tham chiếu đối tượng" như trong các ngôn ngữ OO khác). Các phương thức mutator sẽ phải là các phương thức của kiểu con trỏ tới cấu trúc thay vì kiểu struct, để thay đổi các trường của "đối tượng", vì vậy thuận tiện để có một con trỏ tới cấu trúc thay vì cấu trúc giá trị chính nó, để tất cả "phương thức" sẽ nằm trong tập hợp phương thức của nó.

Ví dụ, để bắt chước một cái gì đó như thế này trong Java:

class Car { 
    String make; 
    String model; 
    public Car(String myMake) { make = myMake; } 
    public setMake(String newMake) { make = myMake; } 
} 

Bạn thường sẽ thấy một cái gì đó như thế này trong Go:

type Car struct { 
    make string 
    model string 
} 
func NewCar(myMake string) *Car { 
    return &Car{myMake, ""} 
} 
func (self *Car) setMake(newMake string) { 
    self.make = newMake 
} 
+0

vì vậy, với đi, tôi không nên sử dụng từ khóa 'mới'? tôi chỉ nên trả lại một tham chiếu như bạn đã minh họa? Tôi đang bối rối bởi ví dụ của bạn. – Carson

+0

@Carson: tốt, bạn có thể sử dụng 'mới', nhưng' mới' khởi tạo giá trị cho giá trị bằng không của loại, cho cấu trúc là tất cả các trường được khởi tạo thành giá trị bằng không. Đó có thể không phải là giá trị được khởi tạo hợp lệ cho tất cả các loại. Nếu giá trị bằng không là tốt để khởi tạo kiểu của bạn, thì bạn chỉ có thể sử dụng 'new'; nhưng nếu loại của bạn cần một "nhà xây dựng" tùy chỉnh, thì bạn nên xác định chức năng của riêng mình như tôi đã cho thấy – newacct

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