<------- 기본기 ------->
Static: 전역함수
배열은 어떤 특징을 그룹짓고 메모리 공간을 연속적으로 써서 랜덤엑세스가 가능하게 한다.
class는 할당 & 참조
// 클래스 정의
class obj
{
int a;
char b;
void foo() { ... }
};
// 클래스의 변수 선언
obj myobj;
// 함수 호출
myobj.foo();
struct은 복사 & 참조는 하지 않는다
// 구조체 정의
struct data
{
int a;
char b;
void foo() { ... }
};
// 구조체의 변수 선언
data mydata;
// 함수 호출
mydata.foo();
구조는 기본적으로 갖지만, main에서 호출하면 class는 호출되지 않는다.
class의 기본 접근 지정자가 private이기 때문,
struct은 public이다.
그렇지만 보통 struct은 데이터 모음인 Plain Old Data(POD)로 사용한다.
struct newStruct
{
// 데이터의 모음
int data;
int a;
int b;
}
그외에 private, protected멤버나 여러 로직을 추가 구현해야 할 경우에 class를 사용한다.
C#에선?
C#에세도 struct와 class는 멤버 변수와 메서드를 동일하게 가질 수 있지만,
struct | class |
값 타입(Value type) | 참조 타입(Reference type) |
스택에 할당 | 힙에 할당 |
상속 불가능 | 상속 가능 |
값 타입은 함수의 매개변수로 전달했을 때, 원본 값의 복사본을 전달
=> 전달받은 값을 변경해도 원본에는 영향을 주지 않음
+ 전달 될 때 스택 영역의 구조체의 크기만큼 할당하기 때문에 크기가 크면 스택의 사용량도 늘어남
참조 타입은 원본을 참조할 수 있는 주소 전달
=>참조 타입은 해당 주소에 있는 원본을 수정
+ 처음에 힙에 할당하고 이후 전달할 때는 주소 값만 전달하므로 클래스의 크기가 커도 스택 사용량은 늘지 않음
이와 비슷하게 Unity에서는 vector3, Quaternion 등의 구조체를 사용하고 있는데 일반적으로 new키워드를 사용하면
힙에 할당 되는 것과 달리 스택에 할당된다
가비지컬렉터
C#의 특성상 힙에 할당된 메모리는 가비지 컬렉터라는 것이 사용하지 않는 애들을 정리 해주는데, 처리해야할 양이 많으면 프로그램 동작 속도가 느려지거나 아에 멈추기도 한다. (스택은 사용후 종료됨)
그래서 힙에 할당하지 않아도 되는건 스택에 사용하면 프로그램 속도 향상에 도움됨 but, 스택 메모리는 제한이 있어 많이 사용하면 스택오버플로우가 발생할 수 있으니 적절하게 균형있게 사용하는 것이 좋다.
가상(virtual)
가상과 추상사이에 핵심적인 차이점은 완성도이다. 가상 클래스는 재정의(override)할 수 있지만 필수는 아니다. 하지만 추상 클래스는 파생 클래스에서 의무적으로 재정의해야 한다.
// 가상클래스
public class Animal
{
public virtual void Speak()
{
Debug.Log("Hello!"); // 가상 클래스는 구체적인 기능을 기술할 수 있다.
}
}
public class Person : Animal
{
// 가상 클래스의 파생 클래스는 꼭 재정의하지 않아도 된다.
}
// 추상클래스
public abstract class Animal
{
public abstract void Speak(); // 추상 클래스는 구체적인 기능을 서술할 수 없다.
}
public class Person : Animal
{
public override void Speak()
{
Debug.Log("안녕!");
}
}
더 자세하게는 따로 설명을 하고자 한다
추상클래스(Abstract Class)
추상화는 객체지향언어의 출발점이자 기본 원칙이다. 객체를 만드는 추상적인 과정이나 사용자에게 기본적인 내용만 보여주고 숨겨야할 데이터는 나타내지 않는다.
추상클래스는 인스턴스(객체)를 만들 수 없는 특별한 클래스이다.
추상클래스는 추상메서드를 만들고 상속을 통해 파생클래스에서 구현하도록 하는 것이 목적이다.
using UnityEngine;
public class AbstractExample : MonoBehaviour
{
void Start()
{
Dog doggy = new Dog();
Cat kitty = new Cat();
doggy.Eat()
kitty.Sleep()
doggy.MakeSound()
kitty.MakeSound()
/*
냠
zzz
멍
냥
*/
}
}
//보통 클래스의 경우
class Pet
{
public void Eat()
{
Debug.Log("냠")
}
public void Sleep()
{
Debug.Log("zzz")
}
public void MakeSound() //각자 소리가 달라 곤란
{}
}
//추상 클래스의 경우
abstract class Pet
{
public void Eat()
{
Debug.Log("냠")
}
public void Sleep()
{
Debug.Log("zzz")
}
public abstract void MakeSound();
}
class Dog : Pet
{
public override void MakeSound() // 추상메서드로 선언해줬기 때문에 내부에서 구현부분을 작성
{
Debug.Log("멍")
}
}
class Cat : pet
{
public override void MakeSound() // 추상메서드로 선언해줬기 때문에 내부에서 구현부분을 작성
{
Debug.Log("냥")
}
}
추상 클래스는 인스턴스를 만들 수 없는데 그 이유는 무엇일까
인터페이스(Interface)
클래스는 하나의 부모클래스만 상속할 수 있어 여러 부모클래스로 부터 상속받을 수 있는 '다중 상속'을 할 수 없다.
인터페이스는 클래스가 다중상속을 할 수 없는 단점을 보완하기 위해 만들어진 개념이다.
클래스는 인터페이스를 여러 개 상속받을 수 있다.
인터페이스의 구조는 추상클래스와 비슷한데 공통점은 추상화를 사용한다는 것이다.
인터페이스 이름은 클래스명과 구별할 수 있도록 이름 앞에 대문자 I (아이)를 붙여 만든다.
메서드, 프로퍼티(property 속성), 인덱서(indexer) 등을 멤버로 가질 수 있다.
멤버는 모두 추상화만 가능하다. 즉, 멤버 선언만 할 수 있다.
추상 클래스와 마찬가지로 인스턴스(객체)를 생성할 수 없다.
인터페이스의 다중 상속 테스트
using UnityEngine;
class InterfaceExample : MonoBehaviour
{
void Start()
{
Knight Tom = new Knight();
tom.StrikingPower = 90.ToString();
Debug.Log(tom.StrikingPower);
Tom.Attack();
Tom.Defend();
/*log
90
Attack!!
Defend!!
*/
}
}
interface ISword
{
string StrikingPower { get; set; } // 자동 구현 프로퍼티
void Attack();
}
interface IShield{
string DfensivePower { get; set; } // 자동 구현 프로퍼티
void Defend();
}
class Knight : ISword, IShield
{
public string StrikingPower { get; set; }
public string DfensivePower { get; set; }
public void Attack(){
Debug.Log("Attack!!");
}
public void Defend(){
Debug.Log("Defend!!");
}
}
자동 구현 프로퍼티는 필드에 접근하기 위한 메서드를 구현하지 않고 단지 값을 가져오거나 저장하는 용도로 사용
인터페이스는 멤버의 추상 선언만 가능하므로 abstract키워드는 생략한다. 또한 모든 멤버는 public으로 처리하므로 접근 제한자도 생략.
Knight라는 클래스를 만들고 인터페이스를 상속하기 위해 콜론( : )을 사용해서 상속받아 구현,
클래스의 실행 부분에 인터페이스의 멤버를 public으로 구현
인터페이스 사용
using UnityEngine;
class InterfaceTest : MonoBehaviour
{
void Start()
{
IHero heroTest = gameObject.GetComponent<IHero>();
heroTest.initialize();
heroTest.ApplyDamage(20);
heroTest.health += 10;
Debug.Log(heroTest.health);
// 90
}
public float health { get; set; }
public void Initialize()
{
health = 100;
}
public void ApplyDamage (float points)
{
health -= points;
}
}
interface IHero
{
void Initialize(); //추상 멤버
float health { get; set; } //프로퍼티
void ApplyDamage(float points); //points매개변수를 가지고 있는 메서드
}
gameObject의 컴퍼넌트로 인터페이스를 호출하여 IHero타입의 변수 test에 저장할 수 있다.
(인터페이스는 멤버에 접근하기 위한 객체를 만들 수 없다. )
GetComponent 메서드는 다른 컴퍼넌트를 가져올 때 사용, 이때 컴퍼넌트의 타입(Type)을 제네릭 < > 기호에 넣으면 된다.
위와 같은 스크립트의 경우는 유니티의 base클래스인 MonoBehaviour 클래스를 상속받은 클래스를 gameObject(게임오브젝트)에 연결하면 컴퍼넌트로 사용할 수 있다. 그래서 객체를 만들 수 없는 IHero인터페이스도 호출할 수 있는 것이다.
인터페이스는
- 다형성(상속)이 가능,
- 추상클래스의 한계를 넘어 다중상속이 가능
- 넓고 얕게 연결 가능
이렇게 연결된 클래스들은 밀접한 관계가 아니기 때문에 제작시에 변경하거나 분리해야 할 때 복잡하고 심각한 에러를 일으킬 확률이 줄어들고 유연하게 대처할 수 있는 기회를 제공.
'IT > 공부 정리' 카테고리의 다른 글
[Git] 깃 던전 공략법 (2) | 2024.09.09 |
---|---|
[Unity] 유니티복습 생명주기 (0) | 2024.06.19 |
[Unity] HTTP 통신 (0) | 2023.06.16 |
MQTT, MQTT Protocol란 (0) | 2023.06.16 |
[UNITY/C#] Catmull-Rom 스플라인을 사용하여 곡선 및 텍스처에 적용하기 (1) | 2023.03.19 |