구조체와 메소?서?드

최승훈·2020년 8월 10일
0

Golang기초

목록 보기
4/7
post-thumbnail

1.구조체

구조체란?

하나 이상의 변수를 묶어서 새로운 자료형을 정의하는 Custom data type
필드들의 묶음이며, 필드들의 컨테이너 역할을 함.

Golang에서의 클래스는 Custom Type을 정의하는 구조체로 표현됨.
전통적인 객체지향의 클래스가 필드와 메서드를 가지는것과는 다르게 구조체는 필드만 가지고,
메소드는 별도로 분리하여 정의함.

구조체도 Custom Type이기 때문에 type키워드를 사용해서 구조체를 정의함.


type Pet struct {
 	name string;
    age int;
}

구조체의 선언은 단순시 형태의 선언일 뿐,그 형태를 이용해 아무것도 만들지않으면 의미가 없음.
따라서, 구조체는 선언 후 객체를 생성하여 사용할 수 있음.

구조체 선언하는 방법

객체이름 := 구조체이름{필드에 저장할 값}

필드에 저장할 값들을 아무것도 지정해주지 않는다면, 빈 객체가 생성됨.

빈 객체의 필드 값을 설정하는 방법

객체이름.필드이름 = 저장할 값

빈 객체 혹은 일부의 필드가 생략된 상태로 생성된 객체의 해당 필드값들은 ZeroValue(정수인 경우 0, float인경우 0.0, string인 경우 "", 포인터의 경우 nil)이 설정됨.

예시코드

package main

import "fmt"

type LolPlayer struct {
	name string
	age int
	position string
}

func main () {

	// 필드설정을 안한 빈 객체 생성
	p := LolPlayer{}
    fmt.Println(p)
	// 필드의 값들을 .을 통해 지정
	p.name = "show"
	p.age = 30
	p.position = "bot"
	fmt.Println(p)

	// 객체의 생성과 동시에 필드의 값을 지정해 주는 경우, 필드의 순서대로 값을 지정해줘야 함.
	p1 := LolPlayer{"niki",20,"mid"}
	fmt.Println(p1)

	// 객체의 생성과 동시에 필드의 값을 지정해 주는 경우, 필드명을 명시할 시 순서는 상관없어짐.
	p2 := LolPlayer{age: 20, name: "ii", position: "top"}
	fmt.Println(p2)

	// 객체 생성이후에도 필드의 값을 변경 할 수 있음.
	p3 := LolPlayer{name: "Ryu"}
	fmt.Println(p3)
	p3.name = "Ryuuuu"
	p3.position = "mid"
	p3.age = 20

	fmt.Println(p3)
}

Golang의 구조체는 기본적으로 'mutable' 개체로서 필드 값이 변화할 경우 별도로 새 객체를 만들어 내지 않고 해당 객체의 메모리에서 직접 변경이 됨.

구조체 포인터

함수에서 매개변수로 전달된 값을 복사해서 지역변수로 사용하는 경우가 아닌,
매개변수로 전달된 원래의 값의 주소를 참조해 값이 지정된 주소에 직접 접근하는 경우(매개변수에 "&"기호를 붙여 Pass by reference) 포인터를 사용했음.

구조체도 마찬가지로 '구조체 포인터'를 만들 수 있음.

1. new(구조체이름) 을 사용하여 객체를 생성하기.
2. 구조체 이름 앞에 '&'기호를 붙이기.

다른 자료형의 포인터들은 역참조를 위해 '' 를 사용한 반면, 구조체는 선언만 하면 자동으로 역참조가 되므로 함수 안에서 '' 를 사용할 필요가 없음.

package main

import "fmt"

type LolPlayer struct {
	name string
	age int
	position string
}

// pass by reference
func addByReference(l *LolPlayer) {
	l.age += 30
}

// pass by value
func addByValue(l LolPlayer) {
	l.age += 30
}


func main () {
  // 1. new(구조체이름) 을 사용하여 객체를 생성하기.
  p1 := new(LolPlayer)
  p2 := LolPlayer{name: "show"}
  p1.age = 20
  p2.age = 30

  fmt.Println(p1, p2)
  // LolPlayer pointer 를 매개변수로 받고있지만 &를 쓰지 않아도 됨
  addByReference(p1)

  addByValue(p2)

  fmt.Println(p1, p2)
  // 2. 구조체 이름 앞에 '&'기호를 붙이기.
  // &을 붙이면 pass by reference도 가능.
  addByReference(&p2)
  fmt.Println(p1, p2)
}

생성자 함수(construcor)

사전에 미리 구조체의 필드를 초기화 해둘 필요가 있을경우(필드로 map등이 들어있을경우)미리 해당 구조체의 객체 생성자 함수를 선언 해두면 외부에서 해당 구조체를 사용할 때 필드(맵등의)도 같이 초기화 해야하는 번거러움을 줄일수 있음.

생성자 함수는 호출하면 구조체의 객체생성 및 초기화, 입력한 필드 생성 및 초기화를 함과 동시에 객체를 반환함.

package main

import "fmt"

type LolPlayer struct {
	name string
	age int
	position string
	result map[int]string
}

func NewLoLPlayer() *LolPlayer {
	p := LolPlayer{}
	p. result = map[int]string{}
	return &p
}

func main () {
    // p1같은경우 생성자 함수로 reuslt필드를 초기화.
	p1 := NewLoLPlayer()
    // p2같은경우 객체만 생성함.
	p2 := LolPlayer{}
	fmt.Println(p1, p2)
		
  	// 생성자 함수에서 result의 map을 초가화 해두었기 때문에 바로 값을 저장할 수 있음.
	p1.result[0] = "win"
	// 바로 맵에 값을 추가하려고하면 맵이 초기화 되어있지않기때문에 nil포인트 에러가 남.
	//p2.result[0] = "lose"
	p2.result = map[int]string{}
	p2.result[0] = "lose"
	fmt.Println(p1, p2)

}

2.메소드

Golang에서의 메소드란?

특정 속성들의 기능을 수행하기 위해 마들어진 특별한 함수를 메소드 라고 함.
Java에서는 이들을 한 곳에 묶은 클래스 안에서 메소드가 있지만, Golang에서는 구조체 내에서 메소드를 선언하지 않고 일반 함수처럼 별도 분리한 형태로 선언.

메소드는 구조체의 필드들을 이용해 특정 기능을 하는 함수.

구조체의 메소드 선언방법

func (매개변수이름 구조체이름) 메소드이름() 반환형 {}

매개변수 이름은 구조체 변수명으로서 메소드 내에게 매개변수처럼 활용됨.
func 뒤에 함수이름을 입력하지 않고 구조체 이름 뒤에 메소드 이름을 입력.

예시 코드

package main

import "fmt"

// 너비와 높이를 필드로 가지는 삼각형 구조체 선언
type triangle struct {
	width, height float32
}

// 넓이를 구하는 구조체 메소드 작성.
// value receiver
func (t triangle) getArea() float32 {
	return t.width * t.height / 2
}

func main() {
	t1 := triangle{width: 30, height: 20}
	t1Area := t1.getArea()
	fmt.Printf("삼각형 t1 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f \n", t1.width, t1.height, t1Area)
	t2 := new(triangle)
	t2.height = 20
	t2.width = 15
	t2Area := t2.getArea()
	fmt.Printf("삼각형 t1 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f \n", t2.width, t2.height, t2Area)
}

위 코드의 value receiver (t triangle) 는 어떤 구조체를 전달 받는지 명시하는 receiver.
구조체의 객체정보를 전달 받아, 메소드의 기능을 수행하는 것.
함수를 사용해서 매개변수로서 객체를 활용하는 것과는 다른 모습.
t1Area := t1.getArea()를 보면 일반적인 함수호출과는 다른것을 확인할 수 있음.

Value Receiver과 Pointer Receiver

위의 예시코드에서는 메소드가 객체의 값(value)를 전달(복사)받아 연산한 후 넓이 값을 반환하고있음.
포인터 정보를 전달하면 객채의 필드값을 메소드에서 직접 접근해 수정할 수 있음.
메소드의 receiver부분에서 주솟값을 참조하는 연산자인 *를 구조체 이름 앞에 붙여주면
PointerReciever가 됨.

package main

import "fmt"

// 너비와 높이를 필드로 가지는 삼각형 구조체 선언
type triangle struct {
	width, height float32
}

// 넓이를 구하는 구조체 메소드 작성.
// value receiver
func (t triangle) getAreaByValue() float32 {
    // 객체의 값을 복사 받은 다음 연산을 하고 있기 때문에 메소드를 부른 기존 객체의 width값은 변경이 되지 않음.
  	// t 라는 지역변수가 하나 새로 생겼다고 생각하면 됨.
	t.width += 10
	return t.width * t.height / 2
}

// pointer receiver
func (t *triangle) getAreaByReference ()float32 {
      // 객체의 포인터를 받은 다음 연산을 하고 있기 때문에 메소드를 부른 기존 객체의 width값은 변경됨.
	t.width += 10
	return t.width * t.height / 2
}

func main() {
	t1 := triangle{width: 30, height: 20}
	t1Area := t1.getAreaByValue()
	fmt.Println(t1)
	fmt.Printf("삼각형 t1 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f \n", t1.width, t1.height, t1Area)

	t2 := new(triangle)
	t2.height = 20
	t2.width = 15
	t2Area := t2.getAreaByReference()
	fmt.Println(t2)
	fmt.Printf("삼각형 t1 너비:%.2f, 높이:%.2f 일때, 넓이:%.2f \n", t2.width, t2.height, t2Area)
}

출처: https://edu.goorm.io/learn/lecture/2010/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-%EA%B3%A0%EB%9E%AD-%EA%B8%B0%EC%B4%88/lesson/316444/%EC%84%B1%EC%A0%81-%EC%A0%80%EC%9E%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8

profile
안녕하세요

0개의 댓글