[C++] 객체지향프로그래밍1 : con(de, delegating)structor / overloading / include guard (Chapter 8 - 12)
이 게시글은 대학교 객체지향프로그래밍1 (C++) 강의를 수강하며 교수님의 강의를 정리한 것입니다. 오직 개인적인 학습 기록으로 활용하기 위한 용도로 작성되었으며, 가독성이나 체계적인 정리가 부족할 수 있습니다.
추상화 (abstraction)
캡슐화 (encapsulation)
상속 (inheritance)
다형성 (polymorphism)
생성자와 소멸자 정리 ( constructor & destructor )
생성자 : 객체를 초기화 하는 역할
객체 변수 정의 시 묵시적으로 생성자가 호출
생성자를 하나라도 추가하면 기본 생성자는 만들지 않는다.
복사/이동 생성자, 복사/이동 대입 연산자, 소멸자 중 하나 라도 만들면 모든 것들 전부 만들지 않는다.
기본 생성자 ( constructor )
기본 생성자는 default 를 추가하여 기본 구현 가능.
변환 생성자 ( conversion constructor )
생성자 중 매개변수가 하나 뿐인 경우 묵시적 변환에 이용될 수 있음.
explicit 키워드로 이를 막을 수 있음.
1
explicit Student (int id) { cout << "ctor called" << endl; }
복사 생성자 ( copy constructor )
복사 생성자는 const 참조를 매개변수로 갖는다.
컴파일러가 만들어주는 복사 생성자는 모든 멤버를 얕은 복사 ( shallow copy ) 한다.
1
Student ( const Student &ref);
이동 생성자 ( move constructor )
복사 생성자는 원복 객체로부터 값을 복사했다.
멤버 변수를 원본 객체로부터 새 객체로 “이동” 시키길 원할 때 이동 생성자를 사용.
- 복사의 부담이 클 때
- 오직 한 객체만이 어떤 자원을 “소유” 해야될 때
1
2
3
4
class A {
public:
A(A &&ref);
};
멤버 변수를 원본 객체로부터 새 객체로 “ 이동 “
1
2
3
Student(Student && ref) : ptr{ref.ptr} {
ref.ptr = nullptr;
}
std::move : lvalue를 rvalue 참조로
std::move(lvalue)
1
2
3
4
5
int main (void) {
A org;
A moved{std::move(org)};
}
L-Value R-Value
- lvalue reference
계속해서 갖고 있을 객체에 대한 참조
lvalue 참조 : 타입 & 참조이름 = 객체; - rvalue reference
계속해서 갖고 있을 생각이 없는 객체에 대한 참조 (임시객체)
객체 속성을 다른 객체에 이전시킬 때, 사라질 운명의 객체에 대하여 사용
**rvalue 참조 : 타입 && 변수명 { rvalue }; **
소멸자 (destructor)
객체가 사용하고 있던 자원을 정리하는 역할
동적 할당된 메모리, 기타 리소스 의 해제
KEYWORD
Const keyword
읽기 목적의 함수 매개변수의 참조에는 const.
const 멤버 함수
클래스 멤버 함수 뒤에 const 키워드를 붙일 수 있다.
이 멤버 함수는 객체의 속성을 고치지 않아 라는 뜻이 된다.
1
2
3
4
class MyClass {
public:
void f(int) const {}
};
** 따라서 const 객체는 오직 const 가 붙은 멤버 함수만 호출할 수 있다. **
const_cast
const 객체를 쓰다가 non-const 객체로 취급해야될 때가 있다.
타입 변경 없이 const 적용 여부만 제어하는 용도로 사용
1
2
MyClass& ref = const_cast<MyClass &>(const_obj);
static keyword
extern : “ 어딘가에 그런게 있어 “
여러개의 소스파일을 다룰 때, 다른 파일 안의 변수/함수를 접근할 때 사용
“ 다른 오브젝트 파일에 그런게 있어 “ 라는 의미가 된다.
extern 으로 지정된 것들은 나중에 linker 가 찾아서 연결.
변수/함수 앞에 static 을 붙일 수 있다.
1
2
3
static int a;
static void f() {}
- 해당 파일 안에서만 유효하다.
** 변수의 경우에는 함수 안에서도 static을 붙일 수 있는데, 해당 함수 안에서만 유효하다 **
.data 혹은 .bss 영역에 저장.
static 변수의 지속성
전역 영역에 있든 함수 안에 있든 static 변수의 값은 계속 유지된다. 프로그램이 종료될 때 소멸한다.
- 전역 영역의 static 변수 : 프로그램이 실행될 때 생성, 프로그램이 종료될 때 소멸.
- 함수 내부의 static 변수 : 함수가 최초로 호출 될 때 생성, 프로그램이 종료될 때 소멸.
Class의 static 멤버
static 멤버 변수는 ** 이 클래스 및 자손 클래스의 모든 객체가 공유한다 ** 라는 뜻
전역 영역에 그 변수를 실제로 정의해줘야 된다.
static 멤버 함수에서는 this 포인터를 쓸 수 없다.
Class 의 static 멤버의 용도
- 객체 간에 공유되는 변수를 만들 때
- 객체의 멤버 변수로 만들기에는 객체의 본질과 직접 관련 x
- 전역 변수나 전역 함수를 연관된 클래스 내부로 캡슐화 할 때
- 이름 충돌 막고 싶을 때
friend keyword
inline keyword
default keyword
오버로딩 (overloading)
함수 오버로딩
- 이름은 같지만
- 서로 다른 매개 변수 타입을 위한 함수들을 정의하는 것
연산자 오버로딩 (operator overloading)
추상화와 관련.
연산자 오버로딩은 특정 연산자를 사용자 정의 타입에 대해 커스터마이징 하여, 기본 자료형과 유사하게 사용할 수 있도록 해줌.
대입연산자 오버로딩은 특히 객체의 깊은 복사나 자원 관리가 필요한 경우에 중요. ( 기본적으로 c++ 은 객체 간 대입 시 멤버 변수를 그대로 얕은 복사 수행 )
기본 타입만으로 이루어진 연산자 오버로딩은 할 수 없다.
- 매개 변수 두 개를 받는 free function 형태로 오버로딩
1
Matrix operator+(const Matrix &a, const Matrix &b);
- 클래스의 멤버 함수 형태로 오버로딩
1 2 3 4
class Matrix { public: Matrix operator+(const Matrix &b); }
대입 연산자 오버로딩
1 2 3
Matrix& operator= (const Matrix &ref) { return *this; }
&ref 는 대입 연산의 우변에 있는 객체.
return *this 는 현재 객체의 참조를 반환.
변환 연산자 오버로딩 ( conversoin operator overloading )
사용자 정의 타입 -> 기본 타입 으로의 묵시적 변환이 이루어질 수 있도록 변환 연산자를 오버로딩 할 수 있다.
** cf) class Matrix 가 묵시적으로 int 나 bool 이 되게끔 하기 **
1
2
3
operator int() const { return 0; }
operator bool() const { return true; }
operator double() const { return 0.0; }
변환 연산자 앞에 explicit 붙히기
변환 연산자를 오버로딩 하는 경우 묵시적 형변환 때문에 예기지 못한 결과가 나올 수 있음.
** 변환 연산자 앞에 explicit 을 붙이면 변환 연산자가 명시적으로 쓰이게끔 강제 **
1
2
3
4
5
6
7
8
9
10
11
12
class Matrix {
public:
explicit operator() const;
};
int main (void) {
Matrix a;
if (a) {
cout << " ~ " << endl;
}
bool {a};
}
전자는 묵시적인 경우(오류발생), 후자는 명시적인 경우.
ETC.
함수 선언 (Function Declaration)
- 반환타입
- 함수이름
- 매개변수의 타입들
함수 시그너처 (Function Signature)
함수를 구분하기 위한 정보
- 함수이름
- 매개변수 목록부분
- (C++) 추가적인 속성 지정자
함수 오버로딩 : 이름은 같지만 서로 다른 매개변수 타입을 위한 함수들을 정의하는 것
** const 객체는 오직 const 가 붙은 멤버 함수만 호출 할 수 있다. **
** const 가 아닌 객체는 모든 멤버 함수를 호출할 수 있다. **
멤버 함수의 const 는 시그너처의 일부
초기화 리스트 Initializer list
1
Student(int id) : id{id} { . . . }
- 상수 멤버 변수의 초기화 (const keyword)
- 참조자 멤버 변수 초기화
- 클래스 안에서 다른 클래스 객체 초기화
- 상속 받은 클래스의 메버 변수를 초기화
묵시적 포인터 this
위임생성
멤버 변수와 자기 자신 간 생성자/소멸자 순서
- 생성 시
멤버 변수 생성자 -> 자신의 생성자 - 소멸 시
자신의 소멸자 -> 멤버 변수 소멸자
overloading 된 복수개의 생성자가 있을 때 각 생성자는 비슷한 초기화 코드를 가질 것이다.
1
2
3
4
public:
Person(int id, const char* name) : id(id), name(name) {}
Person() : Person(0, nullptr) {
Person(int id) : Person(id, nullptr) {}
include guard
#define 전처리 지시자
#define A ??? : “A 라는 것은 ??? 이다. 컴파일 전에 ??? 로 치환하라.
기계적으로 1:1 치환된다.
#include 전처리 지시자
다른 파일을 해당 소스 코드 안에 포함시키는 역할을 함.
파일의 내용을 통째로 복사함.
#if(not) defined or #if(n)def
1
2
3
4
5
6
7
#define DEBUG
#ifdef DEBUG
#define PRINT(x) printf(x)
#else
#define PRINT(x)
#endif
#endif 와 쌍을 이룬다.
ifdef 는 그 반대의 경우, 즉 #define 으로 정의된 것이 없다면 참이 된다.
여러 번 #include 되어도 안전한 Header
#include “A.h” 와 #include “B.h” 를 했는데 A.h 와 B.h 가 Common.h 를 #include 하는 경우가 흔하게 발생
#include 는 내용을 통째로 복사하기 때문에 같은 내용이 두번 복사될 것.
“선언” 은 여러번 반복되어도 상관없지만, “정의”는 한번만 나와야 하므로 header file 에 정의가 있는 경우 정의가 중복되어 컴파일 오류가 발생.
(출처 : 컴퓨터공학과 문대경 교수님 강의자료 중 일부 )
#pragma once 라는 지시자를 사용할 수도 있다.( endif 로 끝나지 않는다. )
(출처 : 컴퓨터공학과 문대경 교수님 강의자료 중 일부 )
클래스 코드와 헤더파일 (선언과 구현의 분리 )
헤더파일과 헤더파일을 설명하는 CPP파일
즉, 클래스 정의는 헤더 파일에, 구현은 CPP 파일에.
헤더파일
클래스 정의를 포함하되, inline이 아닌 메서드들의 정의는 제외한다.
CPP 파일
inline 이 아닌 메서드들의 정의 (body, 구현)
static 멤버 변수 정의
구현을 CPP 에 넣을 때 규칙
- 클래스 정의가 들어있는 header 을 include 한다.
- 멤버 변수/함수 이름 앞에는 클래스 이름::형태로 붙힌다. ( :: 는 범위지정연산자 라고 한다.)
```cpp
#include “A.h”
A::A() {}
A &A::operator=(const A &ref) {}


