2017-02-14 12 views
7

Myślę, że rozumiem odpowiedź z here, ale na wszelki wypadek chcę jednoznacznie zapytać o następujące rzeczy (moje przeprosiny, jeśli uważasz, że jest to to samo pytanie, ale dla mnie, czuje się inaczej w sprawie obaw):Kopiowanie odniesienia do wskaźnika lub według wartości

func f() *int { 
    d := 6 
    pD := new(int) 
    pD = &d // option 1 
    *pD = d // option 2 
    return pD 
} 

pierwsza opcja, gdzie po prostu skopiować odwołanie jako wskaźnik wydajności jest mądry, bardziej optymalny (to przypuszczenie edukacyjnych, ale wydaje się oczywiste). Wolałbym tę metodę/wzór.

Druga opcja to (płytka) kopia (?). Przypuszczam, że ta metoda, ponieważ kopiuje, nie mam obawy o GC zamiatanie instancji "d". Często używam tej metody ze względu na moją niepewność (lub ignorancję jako początkującą).

To, czym się martwię (lub bardziej niepewnie) polega na tym, że w pierwszej metodzie (gdzie adres "d" zostanie przeniesiony), GC rozpozna, że ​​(zmienna "d") jest wskaźnik pojemnika, więc nie będzie zamiatać? W związku z tym bezpieczne będzie korzystanie z tej metody zamiast? To znaczy. mogę bezpiecznie przekazać wskaźnik "pD" zwrócony z func 'f()' przez cały czas trwania aplikacji?

referencyjny: https://play.golang.org/p/JWNf5yRd_B

Odpowiedz

9

Nie ma lepszego miejsca na wygląd niż na oficjalnej dokumentacji:

func NewFile(fd int, name string) *File { 
    if fd < 0 { 
     return nil 
    } 
    f := File{fd, name, nil, 0} 
    return &f 
} 

Zauważ, że w przeciwieństwie do C, to doskonale OK, aby powrócić adres zmiennej lokalnej; pamięć związana ze zmienną przetrwa po powrocie funkcji. W rzeczywistości podanie adresu złożonego literału przydziela nową instancję za każdym razem, gdy jest oceniany, więc możemy połączyć te dwa ostatnie wiersze.

(źródło: "Effective Go")

więc pierwsza opcja (powrót wskaźnika do zmiennej lokalnej) jest całkowicie bezpieczny, a nawet zachęcani. Wykonując escape analysis, kompilator może stwierdzić, że zmienna wymyka się z zasięgu lokalnego i zamiast tego przydziela ją do sterty.

3

W skrócie: Nie

pierwsze: Nie ma "referencje" w Go. Zapomnij o tym teraz, inaczej zranisz siebie. Naprawdę. Myślenie o "przez odniesienie" jest zupełnie błędne.

Po drugie: wydajność jest całkowicie taka sama. Zapomnij teraz o tego rodzaju optymalizacji nano. Zwłaszcza jeśli chodzi o int. Jeśli i tylko wtedy, gdy masz problem z wydajnością: zmierz, a następnie zoptymalizuj. Intuicyjnie może się wydawać, że myślenie "Przekazywanie małego wskaźnika o długości 8 bajtów musi być znacznie szybsze niż kopiowanie struktur z 30 lub nawet 100 bajtami". Nie jest, przynajmniej nie jest to takie proste.

Po trzecie: po prostu napisz to func f() *int { d := 6; return &d; }. Nie ma tu potrzeby robienia żadnych wymyślnych tańców.

Po czwarte: Opcja 2 tworzy "głęboką kopię" int. Ale może to być mylące, ponieważ nie ma "płytkich kopii" int, więc nie jestem pewien, czy rozumiem, o co pytasz. Go nie ma pojęcia o głębokiej, płytkiej kopii. Jeśli skopiujesz wartość wskaźnika, wartość wskaźnika zostanie skopiowana. Pamiętasz pierwszy punkt? W Go nie ma żadnych odniesień. Wartość wskaźnika jest wartością po skopiowaniu, masz kopię wartości wskaźnika. Taka kopia nie ma absolutnie nic do wskazanej wartości, zwłaszcza że nie wykonuje kopii. To wskazywałoby, że kopie w Go nie są "głębokie". Zapomnij o głębokiej/płytkiej kopii, gdy mówisz o Go. (Oczywiście można realizować funkcje, które wykonują „głębokie kopią” niestandardowych obiektów)

piąte: Go ma prawidłowopracy garbage collector. Nie ma absolutnie żadnej różnicy, co robisz: podczas gdy obiekt jest na żywo, nie będzie on gromadzony, a kiedy będzie można go zebrać, będzie. Możesz przekazywać, zwracać, kopiować, przekazywać, odbierać adresy, wskaźniki dereferencyjne lub cokolwiek chcesz, to po prostu nie ma znaczenia. GC działa poprawnie. (Chyba, że ​​celowo szukasz bólu i błędów, używając opakowania niebezpiecznego.)

Powiązane problemy