일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- vector capacity
- std::ostream
- operator overloading
- discord bot
- diamond inheritance
- placement new
- std::vector
- increment operator
- virtual function
- std::endl
- this call
- virtual function table
- constructor
- new&delete
- std::cout
- delete function
- conversion constructor
- suffix return type
- return by reference
- virtual destructor
- C++
- 더 지니어스 양면포커
- dynamic_cast
- virtual inheritance
- vector size
- c++ basic practice
- base from member
- member function pointer
- c++ multi chatting room
- pointer to member data
- Today
- Total
I'm FanJae.
[C++] OOP(Object Oriented Programming) I 본문
※ 본 포스트는 코드누리 C++ Basic 강의 내용을 보고 정리한 포스트입니다.
1. OOP(Object Oriented Programming)에 대한 필요성
이 개념은 C++에서 가장 중요한 개념인 '객체 지향 프로그래밍'에 대한 이야기를 해보고자 한다.
OOP의 기본적인 개념과, 접근 지정자(private,public), 캡슐화(Encapsulation) 에 대해서도 얘기하고자 한다.
먼저, 사각형을 그리고(draw), 넓이를 구하는 함수에 대해 생각해보자.
#include <iostream>
int getRectArea(int x1, int y1, int x2, int y2)
{
return (x2-x1) * (y2-y1);
}
void drawRect(int x1, int y1, int x2, int y2)
{
std::cout << "Draw Rect" << std::endl;
}
int main()
{
int n = getRectArea(1, 1, 10, 10);
drawRect(1, 1, 10, 10);
}
- 일반적으로 2개의 점이 있으면 하나의 사각형의 결정이 가능해서 위와 같이 표현 가능하다.
- 이 코드가 약간 복잡해 보이는 이유는 사각형을 다루는데, C/C++에는 사각형이란 타입이 없다.
- 따라서, 사각형 한 개를 int 변수 4개로 표현하여 다소 복잡해 보인다.
※ 사각형이라는 타입이 있으면 된다. -> C 언어에도 struct 문법을 사용하여 구현 가능하다.
① OOP의 핵심개념 1. 필요한 타입을 먼저 설계하자.
struct Rect
{
int left;
int top;
int right;
int bottom;
};
// int getRectArea(Rect r)
int getRectArea(const Rect& r)
{
return (r.right - r.left) * (r.bottom - r.top);
}
// void drawRect(Rect r)
void drawRect(const Rect& Rect r)
{
std::cout << "Draw Rect" << std::endl;
}
int main()
{
Rect rc = {1, 1, 10, 10};
int n = getRectArea(rc);
drawRect(rc);
}
※ 이와 같이 구현하면 사각형 타입을 구현하면 훨씬 더 코드가 간결해 질 수 있다.
추가로, 인자를 넘겨 줄때 참조자를 통해 넘겨주면, 이 값을 할당해주기 위한 복사본을 만들지 않기 때문에 훨씬 더 효율적이다.
이런 점에서 OOP의 첫번쨰 핵심은 "필요한 타입을 먼저 설계하자."이다.
사실 이 개념은 C 언어에서도 설계할 수 있기는 하다. 하지만 이후 개념 부터는 C 언어에서 설계하기 다소 어렵다.
② OOP의 핵심개념 '2. 상태를 나타내는 데이터와 상태를 조작하는 함수를 묶어서 타입을 설계한다.'
- 데이터와 함수가 분리되어 있으면, 데이터를 항상 함수의 인자로 전달해야 한다.
- 사각형 관련 모든 함수는 인자로 Rect를 받아야 한다.
즉, 이러한 형태가 불편하니까. 구조체 안에 함수를 넣자는 것이다.
※ C 언어는 함수를 구조체 안에 넣는 행위가 불가능하다. (물론, 함수 포인터쓰면 가능하겠지만.. 이는 논외로 하자.)
이에 반해 C ++에서는 구조체 안에 함수를 넣는 기능을 제공한다.
struct Rect
{
int left;
int top;
int right;
int bottom;
int getArea()
{
return (r.right - r.left) * (r.bottom - r.top);
}
void draw()
{
std::cout << "Draw Rect" << std::endl;
}
};
int main()
{
Rect rc = {1, 1, 10, 10};
int n = rc.getArea();
draw();
}
- 이와 같이 호출 할 수 있다. 코드를 작성할 때도 상대적으로 간결해지고, 사용법도 쉬워진다.
- 구조체 안에 함수가 들어가기 때문에 함수 이름도 간결하게 작성할 수 있다.
2. Object Oriented Programming의 기본 개념
※ 정리하면, 데이터와 함수를 묶어서 타입을 설계하면, 사용법이 쉬워지고, 다양한 객체지향 문법을 통해서 보다 안전하고, 사용하기 쉬운 타입 설계를 할 수 있다는 것이 기본 개념이다.
3. Object란
"In computer science, an object can be variable, a data structure, a function, or a method. As regions of memory, they contain value and are referenced by identifiers."
- 즉, 컴퓨터 과학에서 객체는 변수, 자료 구조, 함수, 메서드일 수 이다고 하면서 메모리에 존재하고, 이름으로 접근할 수 있는 모든 것을 의미한다.
- 하지만, 실제로 우리가 int n과 같은 primitive type은 객체라고 부르지 않고, 보통 변수라고 한다.
- 보통 사용자가 정의한 Data type인 User define type를 객체라고 칭한다.
struct Rect
{
int left;
int top;
int right;
int bottom;
int getArea() { return (r.right - r.left) * (r.bottom - r.top); }
void draw() { std::cout << "Draw Rect" << std::endl; }
};
int main()
{
Rect rc1 = {1, 1, 5, 5};
Rect rc2 = {3, 3, 8, 8};
Rect rc3 = {3, 3, 8, 8};
std::cout << sizeof(rc1) << std::endl;
rc1.draw();
rc2.draw();
}
- 이들이 실제로 메모리에 할당되는 것을 보면, Struct에는 getArea()와 draw()라는 멤버 함수가 있다. 하지만 객체가 만들어질때마다 이를 생성하는 것은 아니다.
※ 즉, 멤버 함수는 객체의 개수에 상관없이 코드 메모리에 한 개만 만들어진다.
이를 보면 Rc1과 Rc2가 같은 멤버함수를 공유하고 있는데, 인자가 없는것처럼 보인다.
rc1.draw();
rc2.draw();
즉, 하나밖에 없는 draw에서 이를 어떻게 구분하는가의 대한 문제이다.
정확히는 this 포인터라는 것을 통해 접근할 수 있다. 이는 추후에 별도 포스트로 다뤄보고자 한다.
4. 접근지정자와 class
#include <string>
struct Person
{
std::string name;
int age;
};
int main()
{
Person p;
p.age = -10;
}
- 멤버 데이터를 외부에서 직접 접근하면 객체가 잘못된 상태가 접근될 수 있다.
- 따라서 멤버 데이터는 외부의 잘못된 사용으로 부터 보호하는것이 안전하다.
접근 지정자에는 크게 3가지가 있다. private,public,protected가 있는데,
오늘은 private,public에 대해서만 이야기 하고자한다. protected는 추후, 상속 포스트에서 따로 다뤄보고자 한다.
4-1. private
#include <string>
struct Person
{
private:
std::string name;
int age;
void setAge(int value)
{
age = value;
}
};
int main()
{
Person p;
// p.age = -10; // error
p.setAge(-10);
}
- 이렇게 하면 Person 객체의 값을 수정할 때 직접적으로 수정은 불가능해지고, setAge()를 통해서만 수정 가능하다.
- 하지만, setAge()도 지금 private에 해당하기 때문에 외부 접근이 불가능한 상태이다.
- 이에 따라서, setAge()는 public을 사용한다.
- 즉, Private인 경우, 멤버 함수에서만 접근이 가능하며, 멤버가 아닌 함수에서 접근 시 에러가 발생한다.
4-2. public
#include <string>
struct Person
{
private:
std::string name;
int age;
void setAge(int value)
{
age = value;
}
public:
void setAge(int value)
{
if ( value >= 1 && value <= 150)
age = value;
}
};
int main()
{
Person p;
// p.age = -10; // error
p.setAge(-10);
}
- 이렇게 함수를 통해 접근 시키면 잘못된 값이 접근하는 것에 대한 방어 코드의 작성이 가능하다.
- 즉, 잘못된 상태가 되는 것을 막는 것은 가능하다. 이에 따라서 코드가 조금 더 안전해 질 수 있다.
- 모든(멤버와 멤버가 아닌) 함수 에서 접근이 가능하다.
5. 캡슐화(Encapsulation)
- 객체 지향 프로그래밍(OOP)에서 아주 중요한 개념이다.
- 캡슐화는 객체의 내부 상태(데이터)를 외부로부터 숨기고, 접근 지정자를 통해 보호하며, 객체의 내부 데이터는 직접적으로 외부에 접근할 수 없게 만든다.
- 즉, 멤버 데이터는 private영역이나 protected에 놓고 외부의 잘못된 사용으로 부터 보하한다.
- 멤버 데이터의 변경은 잘 정의된 규칙을 가진 멤버 함수를 통해서만 변경 가능하게 하는것이 목적이다.
5-1. struct vs class
struct는 접근 지정자 생략시 기본값이 public이다.
class는 접근 지정자 생략시 기본값이 private이다.
※ 객체 지향적 철학에서는 class가 오히려 더 적합하다고 볼 수 있다.
다른 언어는 이것 외에도 많은 차이가 있으나 C++의 경우 이정도의 차이만 띄고 있다.
접근 지정자의 사용은 언어마다 약간의 차이가 존재한다. 아래는 C#의 예시이다.
class Person
{
private string name;
private int age;
public void setAge(int value)
{
if ( value >= 1 && value < 150 )
age = 150;
}
}
'C++ > Basic' 카테고리의 다른 글
[C++] Vector I (0) | 2024.08.13 |
---|---|
[C++] Constructor / Destructor (0) | 2024.08.13 |
[C++] Explicit casting (0) | 2024.08.11 |
[C++] nullptr (0) | 2024.08.11 |
[C++] Reference (0) | 2024.08.09 |