함수 포인터
함수명 앞에 * 붙여서 함수 포인터! 주소값을 저장한다.
`int (*func)(int a);`
일반변수 메모리 변수와 형식만 다르다! 형식만 함수, 저장하는건 함수의 주소값.
#include <stdio.h>
int Plus(int a, int b) {
return a+b;
}
int Minus(int a, int b) {
return a-b;
}
int main()
{
int a = 20;
int b = 10;
int (*fPtr)(int pa, int pb);
fPtr = Minus; //Plus
int result = fPtr(a,b);
printf("결과값 : %d\n",result);
return 0;
}
//console
결과값 : 10
사용하는 이유?
- 함수의 동적 바인딩을 실행시킨다.
- 정적 바인딩 : 메모리의 크기 및 위치가 컴파일 타임에 결정된다. 미리 정함 `C 방식`
- 동적 바인딩 : 런타임 시점에 결정된다. 실행 중에 정함 `JS 방식`
플러그언 방식의 규격을 잘 사용하기 위해서
구조체
하나 이상의 서로 다른 종류의 변수들을 묶어서 의미상 새로운 데이터 타입을 정의하는 것이다.
vs 클래스? 구조체를 기반으로 사용자가 정의한 데이터타입
연관된 변수들을 하나로 묶어서 관리함으로써 데이터 관리에 유용하다. 변수의 개수가 많아지면, 구조체가 유리하다.
struct std
{
char name[10];
int age;
int height;
};
`std st1`로 구조체 변수를 선언할 수 있다. st1는 name, age, height 세가지 요소를 가진 메모리가 생성된다.
구조체 변수를 통해 구조체 멤버의 값을 참조한다 `st1.name` `st1.age` `st1.height`
* `.`(점)을 사용하는걸 직접 접근 이라고 한다 > JS 에서 객체 요소에 접근할때 사용하는 원리!
공용체 > TypeScript
사용자 정의 자료형
union unTemp
{
char a;
int b;
double c;
}un
구조체와 유사하지만 > 메모리 공간을 공유한다는 차이점이 있다.
구조체의 경우 char 1 + int 4+ double 8 = 13byte의 메모리 공간을 차지하는데
공용체의 경우 double 8byte 제일 큰 요소의 메모리 공간을, 작은 요소들이 공유한다.
열거형 enum
데이터들을 열거한 집합
정수형 상수로 취급한다. 연속적인 데이터를 정의한다.
enum Week
{
sun = 0,
mon,
tue,
wed,
thu,
fri,
sat
};
첫 멤버를 0으로 설정하면, 다음 멤버들은 1씩 증가한다.
메모리 구조
- 코드 영역 : 실행할 명령어들이 순서대로 쌓인다. cpu가 이 영역에서 명령어들을 하나씩 가져다 처리한다.
- 스택 영역 : 스택이란 모든 원소들의 삽입, 삭제를 한쪽 방향에서만 수행하도록 하는 선형 자료구조이다. LIFO
- 지역변수, 매개변수
- 힙 영역 : 큐 동작. 컴퓨터 메모리의 일부가 할당되었다가 회수되는 일의 반복이다. 실행시 할당 메모리를 입력 받는다.
- 데이터 영역 : 전역변수, static 변수가 저장되는 메모리 영역. 이 메모리는 프로그램 종료 시 소멸된다.
동적으로 메모리를 할당
변수에 할당한 데이터 값이 유동적일 경우, 고정하지 말고, 실행 시에 결정할때 사용한다.
런타임에 입력받은 변수를 컴파일타임에 대입하는 형태는 시점 논리에 맞지 않다.
`void* malloc(size_t size);`
`size`에 할당받고싶은 메모리의 크기를 바이트 단위로 작성한다.
`void*` : 타입이 지정되지 않는 포인터. 메모리크기만 할당할테니, 메모리 형태는 포인터가 사용한다.
#include <stdio.h>
int main()
{
int num;
int *std;
printf("학생수 입력 : ");
scanf("%d",&num);
std = (int*)malloc(sizeof(int)*num);
if(std==NULL){
printf("메모리가 할당되지 않았음\n");
return 0;
}
printf("할당된 메모리 크기는 %d 이다.\n",sizeof(int)*num);
free(std); //메모리 해제
return 0;
}
* 객체 지향의 new 연산자도 동적 메모리로 할당하기 때문에 중요하다!
*프레임워크 기반 언어 : 가비지 컬렉터로 자동으로 해제한다.
객체지향 프로그래밍 방식
JS 기반 객체 생성원리. TS, React 기반에서 필수이다.
- 구조적 프로그래밍
- 순차적, 하향식, 폭포수 방식
- 기능적인 기본 단위 : 함수
- 시퀀스가 한 방향으로 흘러간다.
- 수정이 많은 유연한 현재의 프로그래밍 방식과 상반된 개념
- 객체지향 프로그래밍
- 기능적인 기본 단위 : 객체
- 이벤트 기반의 모든 윈도우 프로그램
📝 추상화 Abstraction
대상에서 특징만을뽑아낸것
사물의 특성을 정리해서 필드와 메소드로 표현하는 과정이다.
📝 캡슐화 Encapsulation
외뷰에서 내부를 볼 수 없게 숨긴다는 의미이다. 외부로부터 데이터를 조작할 인터페이스가 필요하다.
추상화된 결과를 하나의 클래스에 포함시키고 보호하는 것이다.
`클래스 = 데이터 + 메소드` 은닉된 데이터를 를 감싸서 외부에서 접근할 수 없도록 한다.
*클래스 내에 정의된 함수 : 메소드, 메소드, 속성, 필드, 프로퍼터
클래스 Class
사용자가 정의한 추상적인 데이터 타입
구조체와 비슷하게 생겼고, 멤버 변수와 멤버 함수로 구성되어있다.
사물 차 > 상태 필드 : 차종, 연료의 종류, 배기량 > 메소드 행위 : 달린다. 멈춘다.
class 클래스 이름
{
생성자
소멸자
변수 선언
메소드 선언
}
React의 state와 메소드가 유사한 개념을 가진다.
접근 지정자
- public : 누구나 접근 가능하다.
- private : 내 클래스 내부에서만 접근 가능하고, 외부에서는 절대 접근할 수 없다. 캡슐화의 개념
- protected, internal, protected internal
객체 선언, 생성
클래스를 통해 선언한 변수이다. `Dog a = new Dog();`
정적 메모리 = Heap 영역에 저장된다. size 크기는 자동 계산 후 할당한다.
a 는 Heap에 저장되어있는 Dog 의 주소값을 저장하고 있는 stack 영역에서 주소값을 가리킨다.
using System;
class Dog{
private int eyes, nose, mouse, ears;
public void bark(){Console.WriteLine("멍멍");}
}
class HelloWorld {
static void Main() {
Dog a = new Dog();
a.bark();
}
}
생성자 constructor
객체 생성시 초기화 전용 메소드. 자동으로 호출된다.
- 클래스 이름과 동일한 함수를 만들고
- return 타입이 필요없다. void 도 사용할 필요 없다.
- 객체 생성 순간 자동으로 호출된다.
public Dog(){
eyes = 0;
nose = 0;
mouse = 0;
ears = 0;
}
📝 상속성 Inheritance
상속을 해주는 부모 클래스, 상속을 받는 자식 클래스. 자식에서 부모로 화살표>
부모로 갈수록 추상적이고, 자식으로 갈 수록 구체적이라고 한다.
이미 완성된 클래스를 다른 클래스에 상속할 수 있다. 부모 클래스로부터 상속받을때, 클래스 이름 끝에 `:`을 붙이고,
자바, JS, TS의 경우 `extends`를 붙인 후 부모 클래스의 이름을 적는다.
using System;
class Dog{
protected int eyes, nose, mouse, ears;
public void bark(){Console.WriteLine("Dog멍멍");}
public Dog(){
eyes = 0;
nose = 0;
mouse = 0;
ears = 0;
}
}
class Pudel : Dog{
public Pudel(){
base.eyes = 2;
Console.WriteLine("푸들 눈:{0}",eyes);
}
}
class HelloWorld {
static void Main() {
Dog a = new Dog();
a.bark();
Pudel pd = new Pudel();
}
}
이때 상속할때 변수를 `private`로 선언하면 접근 제한 오류가 발생할 수 있으니, `protected`로 자식에서의 접근만 허용하고, 외부는 보호하는 모듈로 선언해야한다.
`base` : 부모 객체 나타낸다.
프로젝트 간의 상속도 활용할 수 있다.
📝 다형성 Polymorphism - 오버로딩overloading, 오버라이딩overriding
함수의 이름이 같더라도, 전달인자의 타입이나 개수에 따라 구분된다.
- 오버로딩 overloading
- 이름이 같지만, 내용이 다른경우
- 전달인자 타입이나 개수
- 오버라이딩 overriding = 재정의
- 상속에서 기존의 것을 덮어버린다는 개념.
- `public void bark(){Console.WriteLine("Pudel왈왈");}`
- 함수 포인터를 객체 포인터의 형태로 확장한 것이다.
인터페이스
메소드의 목록만을 가지고 있는 명세 Specification, 사용자 정의 타입이다.
메소드의 목록만 선언하고 구현은 하지 않는다.
다중 상속 가능하다.
자식 클래스는 반드시 오버라이딩 해야 한다. > 상속관계에서만 작성할 수 있다.
인터페이스 선언 : `접근지정자 interface 이름 : 기반인터페이스{}`
상속받는 클래스의 형태 : `접근지정자 class 자식클래스이름 : 인터페이스{}`
public interface Car{
void accelerator();
void brake();
}
public class Tesla : Car{
public void accelerator(){
console.WriteLine("테슬라 악셀");
}
public void brake(){
console.WriteLine("테슬라 브레이크");
}
}
public class BMW : Car{
public void accelerator(){
console.WriteLine("BMW 악셀");
}
public void brake(){
console.WriteLine("BMW 브레이크");
}
}
class HelloWorld {
static void Main() {
Tesla tesla = new tesla();
bmw.accelerator();
mw.brake();
BMW bmw = new BMW();
bmw.accelerator();
bmw.brake();
}
}
사용하는 이유?
- 본체가 정의되지 않는 추상 메소드를 가진다
- 기존의 기능을 추가하거나 수정의 개념보다
- 동일한 개념의 기능을 새롭게 정의하는 기능이다.
- 공동작업시 > 표준을 정하는 역할이다. (설계)
추상 클래스를 상속하는 경우 > 기능의 확장이 목적이다.
인터 페이스를 상속하는 경우 > 기능의 기본 정의, 기본 기능을 구상하는 명세서
> 중간 구현단계에서도 새로운 기능을 명세할때 사용할수도 있다.
반드시 상속해야 하는 기능 `implement`
카비지 컬렉터가 메모리를 자동관리한다. > 내부 단편화 현상 `컴팩션`으로 남아있는 메모리 이동한다.
람다 화살표 함수
익명 메소드 : 콜백 메소드에서 사용하는 문법
- 메소드를 미리 정의하지 않고 사용할때 정의한다.
- 코드가 간결해지고, 내용 자체가 복잡하면 안된다
- 별도의 메소드를 만들지 않으므로 코딩 오버헤드를 줄일 수 있다.
- 람다식에서 사용한다.
class HelloWorld {
static int Add(int a){
return a+1;
}
delegate int CalcDele(int x);
static void Main(){
Console.WriteLine(Add(3));
CalcDele d = delegate (int x){
return x+1;
};
Console.WriteLine(d(3));
}
}
람다식 : 익명 메소드를 더욱 간결하게 만들었다.
C에서 사용되는 방법 `delegate`
`(x, y) => x + y;`
인수 => 표현식, 명령문
⭐ 객체지향 OOP
4가지 특징
- 추상화 Abstraction : 사물의 특성을 정리해서 필드와 메소드로 표현하는 과정
- 캡슐화 Encapsulation : 객체의 속성과 메서드를 하나로 묶고, 외부로부터 특정 부분을 숨기는 것.
- private, public, protected
- 무결성 보호, 재사용성, 유지보수성
- 상속 Inheritance : 새로운 클래스가 기존 클래스의 속성과 메서드를 물려받아 재사용하고, 확장하는 것.
- `자식 extends 부모`
- 캡술화의 접근제한자를 주의해야 한다.
- 계층구조를 가진다.
- Animal 클래스를 상속받아 Dog와 Cat 클래스 구현한다.
- 다형성 Polymorphism : 같은 이름의 메서드가 다양한 방식으로 동작할 수 있게 하는 것
- 오버로딩 overloading : 이름이 같지만, 내용이 다른경우. 컴파일 시점에 결정한다.
- 오버라이딩 overriding : 상속에서 기존의 것을 덮어버린다는 개념. 형태가 완전 같다. 런타임 시점에 결정한다.
5가지 원칙 SOLID
- 단일 책임 원칙 SRP : Single Responsibility Principle : 한 클래스는 하나의 책임만 가져야 한다.
- 개방 폐쇄 원칙 OCP : Open/Closed Principle : 확장에는 열려(Open)있으나 변경에는 닫혀(Closed)있어야 한다.
- 리스코프 치환 원칙 LSP : Liskov Substitution Principle : 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- 인터페이스 분리 원칙 ISP : Interface Segregation Principle : 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 의존관계 역전 원칙 DIP : Dependency Inversion Principle : 추상화에 의존한다. 구체화에 의존하면 안된다.
☑️ 배운 점
각 개념에 대한 깊은 원리, 연결관계를 이해할 수 있었다.
정적 바인딩, 동적 바인딩