1. 포인터
var ptrValue *int 과 같이 선언만 한 빈 포인터형 변수는 바로 사용할 수 없다. new 함수로 메모리를 할당해서 사용한다.
- 선언
var numPtr *int = new(int) //포인터 선언 → new 함수로 메모리에 공간 할당
*numPtr = 1 //*를 사용해 포인터 역참조 하여 값 1 대입
fmt.Println(*numPtr) //1 출력
- 변수 앖에 &를 붙이면 해당 변수의 메모리 주소를 뜻한다.
var num int = 1
var numPtr *int = &num
fmt.Println(numPtr) //동일한 메모리 주소 출력
fmt.Println(&num) //동일한 메모리 주소 출력
- 일반 자료형과 포인터 변수의 매개변수의 영향 비교
1) 일반 자료형인 매개변수 n으로 1을 넘겨주었다. compare 함수 scope 안에 지역변수 n = 3이 영향을 미치지 않는다.
1 출력🐶
func compare(n int) {
n = 3
}
func main(){
var n int = 1
compare(n)
fmt.Println(n) //1 출력
}
2) 포인터형 매개변수n으로 1을 넘겨주었다.
3 출력 🤨
func compare(n *int) {
*n = 3
}
func main(){
var n int = 1
compare(&n)
fmt.Println(n) //3 출력
}
2. 구조체
- 선언
type Rectangle struct {
width int
height int
}
func main (){
//구조체 인스턴스 생성
var rect Rectangle
//지역 변수 형태가 아닌 포인터에 메모리 공간 할당하는 법
var rect1 *Rectangle
rect1 = new(Rectangle)
rect2 := new(Rectangle) //구조체 포인터 선언과 메모리 할당
//구조체 인스턴스 생성과 동시에 값 초기화하기
var rect4 Rectangle = Rectangle{10, 20}
rect5 := Rectangle{45, 62}
rect6 := Rectangle{width: 22, height: 53}
}
- 구조체 생성자 패턴 활용하기
new 함수로 구조체의 메모리를 할당하는 동시에 값을 초기화 하는 방법은 없다. 그라너, 다음과 같은 패턴을 사용하여 다른 언어의 생성자를 흉내낼 수 있다.
type Rectangle struct {
width int
height int
}
func NewRectangle(width, height int) *Rectangle{
return &Rectangle{width, height}
}
func main (){
rect := NewRectangle(22, 44)
fmt.Println(rect)
fmt.Println(*rect)
fmt.Println(&rect)
/*출력
&{22 44}
{22 44}
0xc00000e028*/
}
코드를 줄여 다음과 같은 방식도 가능하다.
rect := &Rectangle{20, 10} //구조체를 초기화한 뒤 메모리 주소를 대임
fmt.Println(rect) // &(20, 10)
Golang에서 지역 변수를 계속 참조하고 있다면 스코프를 벗어나더라도 변수가 해제되지 않는다.
#사각형의 넓이 구하기
type Rectangle struct {
width int
height int
}
func rectangleArea(rect *Rectangle) int { //매개변수로 구조체 포인터를 받음
return rect.width * rect.height
}
func main (){
rect := Rectangle{30, 40}
area := rectangleArea(&rect) //구조체의 포인터를 넘
fmt.Println(area)
}
함수의 매개변수에 구조체 포인터가 아닌 일반적인 형태(구조체 인스턴스)로 넘겨주면 값이 모두 복사되므로 주의해야 한다.
func rectangleScaleA(rect *Rectangle, factor int) {
rect.width = rect.width * factor
rect.height = rect.height * factor
//포인터이므로 원래의 값이 변경
}
func rectangleScaleB(rect Rectangle, factor int) {
rect.width = rect.width * factor
rect.height = rect.height * factor
//값이 복사되었으므로 원래의 값에는 영향을 미치지 않음
}
func main (){
rect1 := Rectangle{30, 40}
rectangleScaleA(&rect1, 10) //구조체의 포인터를 넘김
fmt.Println(rect1)
rect2 := Rectangle{30, 40}
rectangleScaleB(rect2, 10)
fmt.Println(rect2)
/*출력
{300 400}
{30 40}*/
}
#구조체에 메서드 연결하기
func (리시버명 *구조체_타입) 함수명()
리턴값_자료형{}
func (rect *Rectangle) area() int { //리시버 변수 정의(연결 할 구조체 지정)
return rect.width * rect.height
//리시버 변수를 사용하여 현재 인스턴스에 접근할 수 있음
}
func main(){
rect := Rectangle{10, 20}
fmt.Println(rect.area())
}
메서드 안에서는 리시버 변수를 통해 현재 인스턴스의 값에 접근할 수 있다. 그리고 구조체 인스턴스에 .을 사용하여 연결된 메서드를 호출한다.
리시버로 정의한 변수에는 메서드가 포함된 구조체의 인스턴스 포인터가 들어온다. 따라서 리시버 변수를 통해서 현재 인스턴스의 필드 값을 가져오거나 변결 할 수 있다.
즉, 리시버는 C++의 this포인터 또는 Java의 this 키워드와 비슷하게 기능한다.
#구조체 임베딩
구조체에서 임베딩을 사용하면 상속과 같은 효과를 낼 수 있다.
1) 학생이 사람을 가지고 있는 Has-a관계
//사람과 학생 구조체
type Person struct {
name string
age int
}
func (p *Person) greeting() {
fmt.Println("hello~~")
}
type Student struct {
p Person //필드 명
school string
grade int
}
func main() {
var s Student
s.p.greeting() // hello~~
}
2) 학생이 사람이 동일한 Is-a 관계
type Person struct {
name string
age int
}
func (p *Person) greeting() {
fmt.Println("hello~~")
}
type Student struct {
Person // 필드명 없이 타입만 선언하면 포함(Is-a)
school string
grade int
}
func main() {
var s Student
s.Person.greeting() // hello~~
s.greeting() // hello~~
}
임베디드 오버라이딩
func (p *Student) greeting() { //메서드 오버라이딩
fmt.Println("how are you~~")
}
s.greeting() // how are you~~
'백엔드, 기타 > Golang' 카테고리의 다른 글
Golang 4. 고루틴 (0) | 2020.08.23 |
---|---|
Golang 3. 2)인터페이스 (0) | 2020.08.20 |
Golang 2. 함수 2)클로저(closure), 지연호출(defer), 패닉과 복구(panic and recover) (0) | 2020.08.15 |
Golang 2. 함수 1)함수의 리턴값/ 변수,슬라이스,맵에 함수 대응하기/클로저 (0) | 2020.08.15 |
Golang 1. 기본 문법 2)배열, slice, map (0) | 2020.08.13 |