[씹어먹는 C++] 4. 객체 지향
[씹어먹는 C++] 4. 객체 지향
💙ref.
1. 객체 지향
1.1. 객체
- 객체: 변수들과 참고 자료들로 이루어진 소프트웨어 덩어리
- 객체에 포함된 변수 및 함수: 인스턴스 변수, 인스턴스 메소드
- 추상화: 현실 세계의 개념을 컴퓨터가 처리할 수 있도록 적절한 형태로 변환
- 캡슐화: 외부에서 변수 값 변경 불가, 메소드를 통해 간접 접근
- 객체 내부 동작 원리를 몰라도 해당 기능 사용 가능
1.2. 클래스
- 객체의 설계도!
- 어떠한 기능에 대한 틀(내부 구현 x)
- 클래스를 이용해서 만들어진 객체를 인스턴스라고 부름
- 클래스 구성 요소: 멤버 변수, 멤버 함수
private
로 정의된 요소들은 객체 외부에서 접근 불가능- 일반적으로 멤버 변수는
private
, 멤버 함수는public
으로 정의(캡슐화)
- 일반적으로 멤버 변수는
2. 클래스 다루기
2.1. 함수 오버로딩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
void print(int x) { std::cout << "int : " << x << std::endl; }
void print(char x) { std::cout << "char : " << x << std::endl; }
void print(double x) { std::cout << "double : " << x << std::endl; }
int main()
{
int a = 1;
char b = 'c';
double c = 3.2f;
print(a);
print(b);
print(c);
return 0;
}
// 실행 결과
// int : 1
// char : c
// double : 3.2
함수명이 같더라도 인자가 다르면 다른 함수로 판단
컴파일러가 적합한 인자를 가지는 함수를 찾아 호출
정확히 일치하는 타입이 없을 경우 형변환 수행
대상 변환 결과 char
,unsigned char
,short
int
unsigned short
int
,unsigned int
float
double
enum
int
형변환을 수행해도 일치하는 타입의 함수가 없을 경우
대상 변환 결과 임의의 숫자(numeric) 다른 숫자 타입 enum
임의의 숫자 0
포인터, 숫자 포인터 void
포인터유사한 타입의 함수를 찾을 수 없을 경우
모호하다(ambiguous)고 판단해 오류 발생
eg)
error C2668: 'print' : ambiguous call to overloaded function
2.2. 생성자(constructor)
- 객체 생성 시 자동으로 호출되는 함수
- 클래스의 멤버 변수 초기화
- 클래스 사용에 필요한 설정값 정의
- 생성자 정의:
/* 클래스 이름 */ (/* 인자 */) {}
- 생성자 유형
- 암시적 방법(implicit):
Date day(2011, 3, 1);
- 명시적 방법(explicit):
Date day = Date(2012, 3, 1);
- 암시적 방법(implicit):
2.2.1 디폴트 생성자 (Default Constructor)
- 생성자를 명시적으로 정의하지 않았을 경우 컴파일러가 자동으로 추가해주는 생성자
- 개발자가 생성자를 정의하지 않아도 생성자가 호출됨!
- 단, 컴파일러가 생성했으므로 아무런 동작도 하지 않음
- 디폴트 생성자 사용자 정의 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
class Date {
int year_;
int month_; // 1 부터 12 까지.
int day_; // 1 부터 31 까지.
public:
void ShowDate();
Date() // 디폴트 생성자 정의
{
year_ = 2012;
month_ = 7;
day_ = 12;
}
};
C++ 11부터 디폴트 생성자를 명시적으로 정의할 수 있다!
1
2
3
4
5
class Test
{
public:
Test() = default; // 디폴트 생성자 정의 명령어
};
- 생성자 선언 뒤에
= default
를 붙여 디폴트 생성자 명시
2.2.2 생성자 오버로딩
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
#include <iostream>
class Date {
int year_;
int month_; // 1 부터 12 까지.
int day_; // 1 부터 31 까지.
public:
void ShowDate();
Date()
{
std::cout << "기본 생성자 호출!" << std::endl;
year_ = 2012;
month_ = 7;
day_ = 12;
}
Date(int year, int month, int day)
{
std::cout << "인자 3 개인 생성자 호출!" << std::endl;
year_ = year;
month_ = month;
day_ = day;
}
};
- 인자를 다르게 한 여러 개의 생성자를 정의해 오버로딩 구현 가능
3. explicit / mutable
3.1. explicit
- 원하지 않는 암시적 변환을 할 수 없도록 컴파일러에게 명시
예제1 - 암시적 변환 방지
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
31
32
33
34
35
36
37
38
39
40
41
42
43
class MyString
{
char* string_content; // 문자열 데이터를 가리키는 포인터
int string_length; // 문자열 길이
int memory_capacity;
public:
// capacity 만큼 미리 할당함. (explicit 키워드에 주목)
explicit MyString(int capacity);
// 문자열로 부터 생성
MyString(const char* str);
// 복사 생성자
MyString(const MyString& str);
~MyString();
int length() const;
int capacity() const;
};
// .. (생략) ..
void DoSomethingWithString(MyString s)
{
// Do something...
}
int main()
{
DoSomethingWithString(3); // ????
}
// 컴파일 오류
// test5.cc:56:3: error: no matching function for call to 'DoSomethingWithString'
// DoSomethingWithString(3); // ????
// ^~~~~~~~~~~~~~~~~~~~~
// test5.cc:51:6: note: candidate function not viable: no known conversion from 'int' to 'MyString' for 1st argument
// void DoSomethingWithString(MyString s) {
// ^
// 1 error generated.
int capacity
를 받는 생성자가explicit
으로 명시MyString
생성자를explicit
으로 선언해 해당 생성자를 이용한 암시적 변환 방지int
에서MyString
으로 변환 불가능
예제2 -복사 생성자 호출 방지
1
2
MyString s = "abc"; // MyString s("abc")
MyString s = 5; // MyString s(5)
- 컴파일러가 적절한 생성자를 호출
s
에 값을 대입한다는 의미
1
2
MyString s(5); // 허용
MyString s = 5; // 컴파일 오류!
explicit
으로MyString(int capacity)
를 설정할 경우 컴파일 오류 발생(복사 생성자 형태 호출 방지)
3.2. mutable
멤버 변수를 mutable
로 선언할 경우 const
함수의 멤버 변수를 변경할 수 있다.
- 멤버 함수를
const
로 선언하는 이유: 해당 함수는 객체 내부 상태에 영향을 주지 않는다는 의미 표현- eg) 읽기 작업을 수행하는 함수
- 서버의 경우 메모리에 캐시를 만들어 자주 요청되는 데이터를 빠르게 조회
- 즉각 캐시를 갱신하기 위해
const
로 정의된 유저 서버 함수의Cache
를mutable
로 선언해 관리
- 즉각 캐시를 갱신하기 위해
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
class A
{
mutable int data_; // mutable로 선언한 data_
public:
A(int data) : data_(data) {}
void DoSomething(int x) const
{
data_ = x;
}
void PrintData() const { std::cout << "data: " << data_ << std::endl; }
};
int main()
{
A a(10);
a.DoSomething(3);
a.PrintData();
}
mutable
로 선언한data_
의 값이const
함수 내부에서 변경됨mutable
로 선언하지 않을 경우const
함수의 멤버 변수에 값 대입 불가능
This post is licensed under CC BY 4.0 by the author.