답해야 할 질문
- new와 malloc()의 차이가 무엇인가
- Abstraction, 추상화란 무엇인가
- inline 메소드는 어떻게 작동하는가
- enum class는 enum과 무엇이 다른가, 왜 필요한가
여는 글
OOP는 프로그램 디자인에 대한 하나의 방법일 뿐이다. C++은 프로그래머가 OOP적 접근을 더욱 쉽게 할 수 있도록 여러 기능을 추가했다.
- Abstraction
- Encapsulation and data hiding
- Polymorphism
- Inheritance
- Reusability of code
class는 C++가 이러한 특징들을 한데모아 구현한 집합체다.
Procedural and Object-Oriented Programming
- 절차지향적 접근은 먼저 어떤 일을 해야할 지 결정하고 나서 어떻게 데이터를 나타내야 할지 생각한다.
- OOP적 접근은 유저의 인식하는 오브젝트를 생각한다. 그후에 유저가 이 오브젝트를 잘 쓸 수 있도록 오브젝트에 어떤 데이터와 기능을 담아야 하는지 생각한다.
Abstraction and Classes
- 현실은 아주 복잡하다. 이 복잡함을 해결하는 방법 중 하나가 추상화를 통해 단일 개체를 만들어내는 것이다. 인간은 수많은 원자들의 결합이다. 하지만 인간을 그렇게만 이해하면 어렵다. 우리는 인간을 그저 한 명이라고만 이해한다. 그게 쉬우니까.
- 프로그래밍에서도 이 '추상화'는 중요하다. 프로그래머는 유저가 오브젝트를 완전히 이해하지 않아도 상호작용할 수 있도록 인터페이스를 만든다.
What is a Type?
- Type을 선언하는 것은 다음의 3가지를 함께 선언하는 것과 같다.
- Data object를 만드는 데 드는 메모리
- 각 메모리 비트가 어떻게 해석돼야 하는지 (long으로 해석해야 하는지, float로 해석해야 하는지)
- operation, method 등 Data object를 가지고 어떤 일을 할 수 있는지
Classes in C++
- class는 추상화를 유저가 정의한 타입으로 만드는 수단이다.
- interface는 두 시스템 사이에 공유되고 있는 프레임워크다.
Access Control
- private와 public이라는 키워드도 추가됐다. 해당 키워드는 클래스 멤버에 대한 access control을 나타낸다.
- data에 대한 직접 접근을 막는 것이 data hiding이다.
- data hiding은 data에 대한 직접 접근을 막을 뿐만이 아니라 data가 어떻게 나타내지고 있는지 사용자가 알 필요가 없게 한다.
Implementing Class Member Functions
- 멤버 함수를 정의할 때는 scope-resolution operator(::)를 사용해서 해당 함수가 어떤 클래스에 속해 있는지 서술해야 한다.
- 클래스 메소드는 해당 클래스의 private 멤버에 접근할 수 있다.
Inline Methods
- 함수의 호출 과정:
- 스택에 함수로 전달할 매개변수와 함수가 끝난 뒤 돌아갈 주소값을 저장한다.
- 프로그램의 제어가 함수의 위치로 넘어와 함수 내에 선언된 지역 변수도 스택에 저장한다.
- 함수가 끝나면 반환값을 넘겨 준다.
- 프로그램의 제어는 스택에 저장된 돌아갈 반환 주소값으로 이동하여, 스택에 저장된 함수 호출 정보를 제거한다.
- 문제는 생각보다 함수는 비싸다는 것이다.
- 하지만 다음과 같은 상황을 생각해보자.
- 여러가지 이유로 간단한 계산식을 함수로 만들어야 한다.
- 해당 함수는 여러번 호출될 가능성이 있다.
- 이러면 프로그래머는 해당 함수 머리에 inline 키워드를 넣어두면 된다.
- inline 함수는 macro 함수와 유사하게 동작한다. 해당 함수를 함수 안 코드로 치환하는 것이다. 다만 매크로는 전처리기가 처리하지만 inline은 컴파일타임에 컴파일러가 코드를 생성한다.
- 가장 중요한 사실은 요즘 컴파일러는 똑똑해서 대충 알맞게 inline을 붙여준다.
Class Constructors and Destructors
- struct에서 list initialization을 배웠다. 이걸 클래스에도 적용할 수 있을까?
struct thing
{
char* pn;
int m;
};
class Stock
{
public:
char* pn;
int m;
}
thing t = {"wodget", -23}; // VALID
Stock s = {"wodget", -23}; // INVALID
- 이게 안되는 이유는 Stock 클래스에 적절한 생성자를 정의하지 않았기 때문이다. 때문에 다음과 같이 정의하면 list initialization을 쓸 수 있다.
class Stock
{
public:
Stock(char* str, int m)
{
this->str = str;
this->m = m;
}
private:
char* str;
int m;
};
Stock hot_tip = {"Hello", -23}; // C++11
- 생성자를 이용해 객체를 생성하는 방법은 다양하다. 한데 모아서 보자.
Stock st = Stock("hello", -23);
Stock st("hello", -23);
Stock* ptr = new Stock("hello", -23);
// C++11 way
Stock st = {"hello", -23};
Stock st {"hello", -23};
Default Constructors
- 객체를 생성할 때, 따로 입력값을 주지 않으면 Default Constructor가 호출된다.
- 클래스 명세에 Default Constructor를 안 넣고 다른 생성자를 구현했으면 Default Contructor 호출이 금지된다.
- Default Constructor는 괄호를 쓰지 않아도 된다.
Stock* ptr = new Stock; // valid
Destructors
- Constructor가 new를 통해 객체를 생성할 때 호출된다면, Destructor는 delete 연산자로 객체를 삭제할 때 호출된다.
- Destructor는 생성자처럼 반환값 없이 클래스 이름으로 함수를 만들고 앞에 ~를 붙여준다. 매개변수는 받지 않는다.
Stock::~Stock()
{
std::cout << "Bye bye~\n";
}
const Member Functions
class Stock
{
public:
void show();
}
const Stock st = Stock("Hello", -23);
st.show(); // Invalid
- st.show()가 안되는 이유는 st가 const라서 생성 이후로 어떤 변경도 있으면 안되는데, show가 그것을 보장하지 못하기 때문이다. (실제로 클래스 멤버 변수를 바꾸지 않더라도)
- const를 유지하는, 즉 일관성을 유지한다는 보장을 하기 위해서 함수 명세 뒤에 const를 붙일 수 있다.
void show() const;
An Array of Objects
- 다음과 같이 객체 배열을 선언할 수 있다.
Stock myStuff[4];
- 이렇게 하면 Default Constructor가 호출된다. 매개변수를 통한 Constructor를 호출하려면 다음과 같이 배열을 선언해야 한다.
Stock stocks[STKS] = {
Stock("aa", 1),
Stock("bb", 2),
Stock("cc", 3),
Stock("dd", 4)
};
- C에는 local scope와 global scope가 있었다. C++은 새로운 class scope가 추가됐다.
- class scope가 있기 때문에 다른 클래스에서 같은 변수 이름을 쓸 수 있다.
Class Scope Constants
- 클래스에서 상수(constant)가 있으면 편한 경우가 있다. 예를 들어 다음 클래스를 보자.
class Bakery
{
private:
const int Months = 12; // declare a constant? FAILS
double costs[Months];
...
}
- 이렇게 class scope에서 상수를 만드는 방법은 두가지가 있다.
- static const로 변수를 선언한다.
- enum을 활용한다.
- enum을 활용해서 상수를 만드는 방법은 다음과 같다.
class Bakery
{
private:
enum {Month = 12};
double cost[Months];
...
}
Scoped Enumerations (C++11)
- 전통적인 Enumeration은 여러 문제가 있다. 그 중 하나가 다른 enum에서 같은 이름으로 정의하면 충돌한다는 것이다.
enum egg {Small, Medium, Large, Jumbo};
enum t_shirt {Small, Medium, Large, Xlarge};
- 충돌하는 이유는 두 enum이 같은 scope에 있기 때문이다. (scope의 종류 중엔 enum scope는 없다)
- C++11은 이를 위해 enum class를 제공한다. 즉, class scope를 사용해서 해결한다. (enum struct도 동일하다)
enum class egg {Small, Medium, Large, Jumbo};
enum class t_shirt {Small, Medium, Large, XLarge};
egg choice = egg::Large;
t_shirt Floyd = t_shirt::Large;
- 이외에도 enum class는 중요한 기능을 제공한다.
- 암시적 형변환을 금지한다.
- 기본적으로 enum은 내부적으로 int로 구현되는데, 이 자료형을 바꿀 수 있다.
int Frodo = t_shirt::Medium; // Error
int Frodo = int(t_shirt::Medium); // OK
enum class : short pizza {Small, Medium, Large, XLarge};
Abstract Data Types
- Class를 잘 이용하는 방법 중 하나가 ADT를 이용하는 것이다. 어떤 코드들은 타입이 달라져도 거의 동일하게 작동한다. 이 특징을 이용한다.
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10};
Item items[MAX];
...
};
참고
- https://techdifferences.com/difference-between-inline-and-macro.html
- https://boycoding.tistory.com/220
- C++ Primer Plus 6th, Chapter 10
'Coding > Unreal, C++' 카테고리의 다른 글
[C++ Primer Plus] 8. Adventures in Functions (0) | 2022.06.15 |
---|---|
[C++ Primer Plus] 7. Funtions: C++'s Programming Modules (0) | 2022.06.11 |
malloc(), new (0) | 2022.06.09 |
I/O multiplexing: select(), poll(), kqueue() (0) | 2022.05.30 |
[C++ Primer Plus] 4. Compound Types (0) | 2022.05.29 |