Professional C++ - C++의 객체지향 언어 특성 소개 + 프로그램 만들기 (Ch1, Part 3-6)

1.3.1 클래스 정의

  • Class: 객체의 특성을 정의
    • Class 선언 코드는 헤더파일 (.h) 파일에 작성
    • Class 구현 코드는 소스파일 (.cpp, .cc) 파일에 작성
  • Data membermethod 구현 필요
    • public, protected, private으로 구분됨.
      • public: 클래스 외부에서 접근 가능
      • protected: 클래스 상속 관계시 접근 가능
      • private: 클래스 내부에서만 접근 가능
        • 외부에서 접근하게 하려면 getter / setter 함수를 만들면 됨.
    • Data member는 보통 m을 앞에 붙여서 표시함. (data member)
  • Class를 생성하는 생성자 (constructor)가 필요.
    • default constructor, copy constructor, parameterized constructor가 있음.
      • default: 기본 생성자
      • copy constructor: 동일한 class의 다른 instance의 내용을 기반으로 새로운 instance를 만드는 것. 이 때, 포인터의 값들은 깊은 복사가 되야한다. (참조 링크)
      • parameterized constructor: 특정한 방식으로 class를 초기화 하는 방법
    • member initialiser list를 쓰는것이 깔끔하고 보기 좋음.
  • Class를 파괴하는 소멸자 (destructor)도 필요.
    • Raw pointer를 사용했으면 destructor에서 포인터 해제를 해줘야함.
    • 외부 API를 통해서 포인터 작업을 했으면 그것 역시 해제를 해줘야함 (e.g. OpenGL context, GPU 메모리…)
    • 이런게 없으면 안적어도 됨.
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
#include <string>
#include <vector>

class someClass
{
public:
using VecI = std::vector<int>;

someClass() = default; // default constructor. 안 적어도 되지만, default를 적어주면 좀 더 확실하게 표현 가능.
someClass(const someClass& some1) { mPtrInt = new int(*some1.mPtrInt); }; // Copy constructor. 포인터의 deep copy를 함
someClass(const std::string str; const VecI& vec): mStr(str), mVecInt(vec) {}; // parameterised constructor + member initialiser list

~someClass() { delete mPtrInt; };

void foo();
int foo2();
double foo3();

void setStr(const std::string& inputStr) { mStr = inputStr; };
void setVecInt(const VecI& inputVec) { mVecInt = inputVec; };

std::string getStr() { return mStr } const;
VecI getVecInt() { return mVecInt } const;

protected:
//

private:
std::string mStr;
VecI mVecInt;
int* mPtrInt;
}

 

1.3.2 클래스 사용하기

  • Class의 instance는 두가지 방법으로 만들 수 있음
    • Stack
    • Heap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class someClass
{
// 위에 있던거...
}

int main()
{
// Stack 기반
auto instance = someClass();
auto str = instance.getStr();
auto vec = instance.getVecInt();

// Heap 기반
auto instance_p = std::make_unique<someClass>();
auto str2 = instance_p->getStr();
auto vec2 = instance_p->getVecInt();

// 제발 쓰지 말았으면 하는 Heap 기반 방식
auto instance_NEVER_DO_THIS = new someClass();
//...
delete instance_NEVER_DO_THIS;
}

 

1.4 Uniform 초기화

  • 종종 실수로 함수에 변수가 들어가면서 타입이 축소화 될 때가 있다.
    • e.g. double -> int
    • 이 경우에는 정보가 손실되고, 컴파일러 에러 없이 예기치 못한 에러가 생길 수 있다. (최신 컴파일러는 왠만하면 경고 주는듯)
  • 안전하게 하는 방법은 uniform initalization이다.
    • Class 초기화를 할 때 유용하다.
1
2
3
4
5
6
7
8
void foo(int i ){ /*... */ };

int main()
{
int pi = 3.141592654;
foo(3.14); // 3.14가 3으로 줄어들을 수 있음
foo({3.14}); // 3으로 줄어들지 않고 컴파일러 에러를 뱉음
}