Post

[C++] 객체지향프로그래밍1 : con(de, delegating)structor / overloading / include guard (Chapter 8 - 12)

[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 멤버의 용도

  1. 객체 간에 공유되는 변수를 만들 때
  2. 객체의 멤버 변수로 만들기에는 객체의 본질과 직접 관련 x
  3. 전역 변수나 전역 함수를 연관된 클래스 내부로 캡슐화 할 때
  4. 이름 충돌 막고 싶을 때

friend keyword

inline keyword

default keyword

오버로딩 (overloading)

함수 오버로딩

  1. 이름은 같지만
  2. 서로 다른 매개 변수 타입을 위한 함수들을 정의하는 것

연산자 오버로딩 (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)

  1. 반환타입
  2. 함수이름
  3. 매개변수의 타입들

함수 시그너처 (Function Signature)

함수를 구분하기 위한 정보

  1. 함수이름
  2. 매개변수 목록부분
  3. (C++) 추가적인 속성 지정자

함수 오버로딩 : 이름은 같지만 서로 다른 매개변수 타입을 위한 함수들을 정의하는 것

** const 객체는 오직 const 가 붙은 멤버 함수만 호출 할 수 있다. **
** const 가 아닌 객체는 모든 멤버 함수를 호출할 수 있다. **

멤버 함수의 const 는 시그너처의 일부

초기화 리스트 Initializer list

1
Student(int id) : id{id} { . . . }
  1. 상수 멤버 변수의 초기화 (const keyword)
  2. 참조자 멤버 변수 초기화
  3. 클래스 안에서 다른 클래스 객체 초기화
  4. 상속 받은 클래스의 메버 변수를 초기화

묵시적 포인터 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 에 넣을 때 규칙

    1. 클래스 정의가 들어있는 header 을 include 한다.
    2. 멤버 변수/함수 이름 앞에는 클래스 이름::형태로 붙힌다. ( :: 는 범위지정연산자 라고 한다.)

```cpp
#include “A.h”

A::A() {}
A &A::operator=(const A &ref) {}

This post is licensed under CC BY 4.0 by the author.