오늘할일
절차지향과 객체지향 차이점
1. 프로그램 구조
- 절차지향형 언어
- 프로그램이 절차(순서)에 따라 작성됨
- 함수나 절차가 중심임
- 데이터를 함수가 처리하도록 설계됨
- 객체지향형 언어
- 프로그램이 객체와 객체 간의 상호작용으로 구성됨
- 데이터와 함수를 묶어 객체라는 단위로 관리함
2. 데이터 관리 방식
- 절차지향형 언어
- 데이터는 보통 전역적으로 접근할 수 있으며, 여러 함수에서 공유됨
- 데이터의 무결성을 보장하기 어려움
- 객체지향형 언어
- 데이터는 객체 내부에 캡슐화되어 접근 제어가 가능함
- 데이터의 보안과 무결성을 보장할 수 있음
3. 재사용성과 유지보수
- 절차지향형 언어
- 코드의 재사용성이 상대적으로 낮고, 수정이 복잡할 수 있음
- 특정 절차에 의존하는 경우가 많아 변경에 취약함
- 객체지향형 언어
- 객체와 클래스를 활용해 코드 재사용성과 확장성이 뛰어남
- 유지보수와 협업에 용이함
4. 예시 언어
- 절차지향형 언어
- 객체지향형 언어
- Java, Python, C++, C#, Ruby 등
5. 프로그램 설계 패러다임
- 절차지향형 언어
- 하향식 설계(Top-Down) 접근 방식을 사용함
- 객체지향형 언어
- 객체 중심 설계(Object-Centered Design) 접근 방식을 사용함
6. 절차지향에선 객체지향을 구현못하는지?
- C언어와 같은 절차지향 언어에서도 객체지향의 개념을 어느 정도 구현할 수 있음
- 다만, 객체지향 언어처럼 편리하게 사용할 수 있는 기본 문법이나 도구가 제공되지 않기 때문에, 직접 구현해야 하는 경우가 많음
- C언어에서 객체지향의 주요 개념을 구현하는 방법은 아래와 같음
- C언어에서도 객체지향 개념을 구현할 수는 있지만, 객체지향 언어처럼 직관적이고 간단하지는 않음. 이를 보완하기 위해 C++ 같은 언어가 등장했으며, 이는 C의 절차지향적 특성을 기반으로 객체지향을 완벽히 지원함.
- 클래스와 객체
- 구현 방식: 구조체(struct)를 사용하여 데이터를 정의하고, 관련 함수들을 별도로 작성하여 사용함
- 예시:
#include <stdio.h>
typedef struct {
int x, y;
} Point;
void move(Point* p, int dx, int dy) {
p->x += dx;
p->y += dy;
}
void print(Point p) {
printf("Point(%d, %d)\n", p.x, p.y);
}
int main() {
Point p = {0, 0};
print(p);
move(&p, 3, 4);
print(p);
return 0;
}
- 캡슐화
- 구현 방식 : 구조체 내부 데이터를 static으로 정의하거나, 직접 접근을 막고 함수로만 접근하도록 구현함
- 한계 : C언어 자체에서 접근 제한자(private, public)를 제공하지 않아 명시적인 캡슐화는 어려움
- 상속
- 구현 방식: 구조체 안에 다른 구조체를 포함시키는 방식으로 유사한 기능을 구현할 수 있음
- 예시
typedef struct {
int width, height;
} Rectangle;
typedef struct {
Rectangle base;
int radius;
} Circle;
- 다형성
#include <stdio.h>
typedef struct {
void (*draw)(void);
} Shape;
void drawCircle() {
printf("Drawing Circle\n");
}
void drawRectangle() {
printf("Drawing Rectangle\n");
}
int main() {
Shape circle = {drawCircle};
Shape rectangle = {drawRectangle};
circle.draw();
rectangle.draw();
return 0;
}
7. c++와 c#의 객체지향 차이점
- 요약
- C++은 성능과 하드웨어 제어가 중요한 응용 프로그램에서 유리
- C#은 생산성과 개발 속도가 중요한 현대 소프트웨어 개발에 더 적합함
- 두 언어 모두 객체지향적인 설계가 가능하지만, 사용 목적에 따라 선택이 달라짐
- 기본 설계 철학
- C++
- C언어를 확장하여 고성능, 하드웨어 제어, 시스템 프로그래밍에 적합하도록 설계되었음
- 객체지향뿐만 아니라 절차지향도 지원함
- C#
- 마이크로소프트에서 개발한 .NET 플랫폼 위에서 동작하도록 설계되었음
- 순수 객체지향 프로그래밍에 초점이 맞춰져 있으며, 현대적인 소프트웨어 개발(웹, 데스크톱, 게임 등)에 적합함
- 메모리 관리
- C++
- 개발자가 직접 메모리를 할당(new)하고 해제(delete)해야 함
- 스마트 포인터(예: std::shared_ptr, std::unique_ptr)를 사용하면 자동 관리가 가능하지만, 기본적으로 수동 관리가 필요함
- C#
- 가비지 컬렉터(Garbage Collector)가 메모리를 자동으로 관리함
- 개발자가 명시적으로 메모리를 해제할 필요가 없으므로 메모리 누수를 방지하기 쉬움
- 플랫폼 종속성
- C++
- 플랫폼 독립적이지만, 컴파일러와 라이브러리에 따라 코드를 조정해야 할 수 있음
- 다양한 운영체제와 하드웨어에서 실행 가능함
- C#
- 원래 Windows와 .NET 플랫폼에 최적화되었으나, .NET Core와 .NET 5 이후로 크로스 플랫폼을 지원함
- 그러나 런타임 환경(.NET Framework 또는 .NET Core)이 필요함
- 다중 상속
- C++
- 다중 상속을 지원함. 여러 부모 클래스를 상속받을 수 있음
- 다만, 다이아몬드 상속 문제와 같은 복잡성을 해결하기 위해 virtual 키워드 등이 필요함
- C#
- 다중 상속을 지원하지 않음
- 대신, 인터페이스(Interfaces)를 통해 다중 상속과 유사한 기능을 구현함
- 언어 문법 및 기능
- C++
- 포인터와 레퍼런스를 사용하여 하드웨어 제어가 가능하며, 낮은 수준의 프로그래밍에 적합함
- 템플릿(Templates)과 메타프로그래밍이 강력함
- C#
- 델리게이트(Delegates), 람다(Lambda), 이벤트(Event), LINQ 등 현대적이고 직관적인 기능을 지원함
- Reflection을 통해 런타임 시에 타입을 탐색하고 동적으로 동작을 제어할 수 있음
- 실행 성능
- C++
- 컴파일된 바이너리가 직접 실행되므로 네이티브 성능이 뛰어남
- 실시간 시스템 및 고성능이 필요한 응용 프로그램에 적합함
- C#
- IL(Intermediate Language)로 컴파일된 뒤 CLR(Common Language Runtime)에서 실행됨
- 약간의 성능 오버헤드가 있지만, 일반적인 응용 프로그램에서는 무시할 수 있는 수준
- 사용 사례
- C++
- 게임 엔진 개발, 시스템 소프트웨어, 임베디드 시스템, 고성능 애플리케이션 등.
- C#
- 데스크톱 애플리케이션, 웹 애플리케이션, 게임 개발(Unity 엔진), 크로스 플랫폼 모바일 애플리케이션(Xamarin) 등.
메타 프로그래밍
개념
- 메타프로그래밍(Metaprogramming)은 프로그램이 자신의 코드나 다른 코드를 분석, 생성, 수정하는 기술을 말함
- 메타프로그래밍은 유연성과 자동화를 제공하여 복잡한 문제를 해결하는 데 유용하지만, 잘못 사용하면 코드의 가독성과 유지보수성이 저하될 수 있음
- 적절히 활용하면 매우 강력한 도구가 됨
- 핵심 개념
- 코드에 대한 코드(Code about Code)
- 프로그램이 실행 중이거나 컴파일 중에 자신의 구조를 검사하거나 변경할 수 있음
- 동적 동작
- 런타임에 프로그램의 동작을 변경할 수 있음
- ex) 동적으로 함수 생성, 클래스 정의 변경 등
- 컴파일 시간 동작
- 컴파일 과정에서 코드를 생성하거나 최적화함
- ex) 템플릿 메타프로그래밍(C++), 매크로(Preprocessor) 등
메타프로그래밍의 주요 기법
- 리플렉션(Reflection)
- 프로그램이 실행 중에 자신의 구조(클래스, 메소드, 속성 등)를 조사하는 기능.
- 예
// C# Reflection 예제
Type type = typeof(String);
Console.WriteLine(type.Name); // 출력: String
- 코드 생성(Code Generation)
- 런타임이나 컴파일 타임에 새로운 코드를 생성함
- 예: Python의 exec() 함수.
code = "x = 5\ny = x + 10\nprint(y)"
exec(code) # 실행 결과: 15
- 템플릿 메타프로그래밍 (C++)
template<int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static const int value = 1;
};
int main() {
std::cout << Factorial<5>::value; // 출력: 120
}
- 매크로(Macro)
- 코드 일부를 텍스트로 대체하거나 확장하는 기능.
- 예 : C언어의 전처리기 매크로
#define SQUARE(x) ((x) * (x))
printf("%d", SQUARE(5)); // 출력: 25
- 동적 프로그래밍(Dynamic Programming)
- 런타임에 새로운 메소드나 클래스를 생성 및 추가
- 예 : Python에서 클래스 동적 확장
class MyClass:
pass
def dynamic_method(self):
return "Hello, Dynamic!"
MyClass.new_method = dynamic_method
obj = MyClass()
print(obj.new_method()) # 출력: Hello, Dynamic!
메타프로그래밍의 장점
- 유연성
- 코드를 더 일반적이고 재사용 가능하게 작성할 수 있음
- 런타임에 상황에 따라 프로그램을 동적으로 수정할 수 있음
- 생산성 향상
- 반복적이거나 패턴화된 코드를 줄이고, 자동화를 통해 개발 속도를 높일 수 있음
- 강력한 최적화
- 컴파일 시간에 반복적인 작업을 미리 처리하거나 최적화함
메타프로그래밍의 단점
- 복잡성 증가
- 코드가 동적으로 변하거나 생성되기 때문에 디버깅과 유지보수가 어려움
- 성능 이슈
- 런타임에 동적으로 동작하는 경우 실행 속도가 느려질 수 있음
- 의존성 문제
- 메타프로그래밍은 언어별로 구현 방식과 제한이 달라, 특정 플랫폼에 종속될 가능성이 있음
메타프로그래밍이 사용되는 영역
- 코드 자동 생성
- 대규모 반복 작업을 줄이기 위해 코드를 자동으로 생성
- 예: ORM(Object-Relational Mapping) 도구에서 SQL 코드 자동 생성
- 프레임워크 개발
- 리플렉션과 동적 메소드 추가를 활용해 프레임워크의 동작을 확장
- 컴파일러 최적화
- C++ 템플릿 메타프로그래밍처럼 컴파일러 수준에서 최적화 작업 수행