2014-15 Intermediate C++ Study #7

58
2014-15 INTERMEDIATE C++ STUDY #7 옥찬호

description

2014-15 Intermediate C++ Study #7

Transcript of 2014-15 Intermediate C++ Study #7

Page 1: 2014-15 Intermediate C++ Study #7

2014-15 INTERMEDIATEC++ STUDY #7

옥찬호

Page 2: 2014-15 Intermediate C++ Study #7

동적 메모리 할당을 통한 객체 생성SPREADSHEET 클래스, 소멸자를 이용한 메모리 해제, 복제의 대입과 관리

Page 3: 2014-15 Intermediate C++ Study #7

SPREADSHEET 클래스

SpreadsheetCell

Spreadsheet SpreadsheetApplication

지난 시간에 SpreadsheetCell 클래스를 작성했었다.이번 시간에는 Spreadsheet 클래스를 점진적으로 만들어 본다.

Page 4: 2014-15 Intermediate C++ Study #7

SPREADSHEET 클래스

SpreadsheetCell**은 서로 다른 크기의 셀 격자를 가질 수 있기 때문에 포인터 타입을 통해생성자에서 셀 격자의 높이와 너비에 맞추어 동적으로 메모리를 할당할 수 있게 한다.

Page 5: 2014-15 Intermediate C++ Study #7

SPREADSHEET 클래스

2차원 배열의 동적 할당 코드, 그리고 높이 3 / 너비 4 크기의 셀을 갖는 Spreadsheet 객체 생성.

4int mWidth

3int mHeight

스택(Stack) 메모리

SpreadsheetCell** mCells

힙(Heap) 메모리

각 항목은 무명의SpreadsheetCell 포인터다

각 항목은 무명의SpreadsheetCell 포인터다

Spreadsheet s1

Page 6: 2014-15 Intermediate C++ Study #7

SPREADSHEET 클래스

x, y 좌표가 유효한 셀 좌표를 가리키는지 검사하기 위해 inRange()라는 메서드를 이용.

Page 7: 2014-15 Intermediate C++ Study #7

소멸자를 이용한 메모리 해제

동적으로 할당한 메모리는 사용이 끝난 후 반드시 해제를 해주어야 한다.동적으로 할당한 메모리의 해제는 객체 소멸자 안에 수행되는 것이 가장 바람직하다.

소멸자는 클래스의 이름과 같은 이름을 가지되 ~ 기호를 앞에 붙인다.

Page 8: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

복제 생성자와 대입 연산자를 직접 만들지 않으면 컴파일러가 자동으로 생성해 준다.컴파일러가 자동으로 생성한 메서드는 데이터 멤버들에 대해 재귀적으로 복제 생성자 또는 대입 연산자를 호출한다.

단, 기본 데이터 타입에 대해서는 복제 생성자나 대입 연산자 대신 얕은 복제(Shallow Copy)가 일어난다.포인터가 가리키는 데이터는 빼놓고 피상적으로 데이터 멤버의 비트 값을 원본에서 대상으로 복제하기만 한다.

스택(Stack) 메모리 힙(Heap) 메모리

각 항목은 무명의SpreadsheetCell 포인터다

각 항목은 무명의SpreadsheetCell 포인터다

4int mWidth

3int mHeight

SpreadsheetCell** mCells

Spreadsheet s1

4int mWidth

3int mHeight

SpreadsheetCell** mCells

Spreadsheet s

Page 9: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

만약 최악의 상황으로 함수 printSpreadsheet()가 Spreadsheet 객체를 리턴하면스택 객체인 s의 소멸자가 호출되면서 mCells 포인터가 가리키는 메모리를 해제해 버린다.

이제 s1이 가리키는 포인터는 더는 유효하지 않다. 이러한 포인터를 댕글링 포인터(Dangling Pointer)라고 한다.

스택(Stack) 메모리 힙(Heap) 메모리

반환 해제된 메모리4int mWidth

3int mHeight

SpreadsheetCell** mCells

Spreadsheet s1

Page 10: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

s1과 s2 두 객체가 생성된 직후 메모리 상태.

스택(Stack) 메모리 힙(Heap) 메모리

4int mWidth

3int mHeight

SpreadsheetCell** mCells

Spreadsheet s2

2int mWidth

2int mHeight

SpreadsheetCell** mCells

Spreadsheet s1

Page 11: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

대입이 실행되고 난 후의 메모리 상태.

스택(Stack) 메모리 힙(Heap) 메모리

4int mWidth

3int mHeight

SpreadsheetCell** mCells

Spreadsheet s2

2int mWidth

2int mHeight

SpreadsheetCell** mCells

Spreadsheet s1

주인이 없어진 메모리!(Orphaned Memory)

s1, s2의 mCells 포인터가같은 메모리를 가리키는 것은 물론

s1의 mCells 포인터가 가리키고 있던메모리 영역이 주인을 잃어버린다.

이러한 문제를 피하기 위해서는좌변항의 객체가 참조하고 있는 메모리를

반환한 후에 새로 메모리를 준비해깊은 복제(Deep Copy)를 해야 한다.

Page 12: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

Spreadsheet 클래스의 복제 생성자 정의 및 구현.포인터 데이터 멤버 변수 mCells에 대해서는 깊은 복제를 하기 위해

2차원 배열의 포인터를 순회하며 각 배열 항목을 하나씩 모두 복제한다.

객체 생성을 위한 복사 생성자이기 때문에 아직 객체 자체가 존재하지 않는다. → 복제하기 전에 mCells에 대한 메모리 해제를 할 필요가 없다.

Page 13: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

Spreadsheet 클래스의 대입 연산자 정의.어떤 객체가 대입 대상으로 원본을 객체로 받아들일 때는 이미 객체가 생성되어 있고 값도 생성된 상태이다.

대입 연산자가 하는 일은 소멸자와 복제 생성자가 연달아 호출하는 것과 비슷하다.

Page 14: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

Spreadsheet 클래스의 대입 연산자 구현.먼저 자기 자신을 대입하는지 검사한다. 이러한 작업을 하는 이유는 성능 최적화와 오류를 방지하기 위함이다.

만약 자기 자신인지 확인하는 코드가 없다면 자기 자신을 대입하는 코드가 실행될 때프로그램이 잘못된 메모리 접근으로 비정상 종료될 수도 있다.

왜냐하면 대입 대상 객체에서는 보유하고 있던 동적 메모리들을 대입하기 전에먼저 해제하는데, 대상과 원본이 같으므로 이 순간에 원본 객체의 메모리도 해제되어포인터 멤버가 댕글링 포인터가 되어 버린다.

그래서 복제 작업 시 댕글링 포인터를 참조하게 되고 어떤 문제가 발생할지 알 수 없다.

Page 15: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

대입 연산자를 구현하게 되면, 사실상 소멸자, 복제 생성자까지 함께 구현하는 셈이 된다.따라서 대입 연산자, 소멸자, 복제 생성자들은 서로 연관된 코드를 가질 수밖에 없다.

복제 생성자와 대입 연산자는 상당히 유사하므로, 공통적인 작업 부분을 빼두면 중복 코드를 피할 수 있다.

Page 16: 2014-15 Intermediate C++ Study #7

복제와 대입의 관리

클래스에서 동적 메모리 할당을 사용할 때 복제와 대입 때문에 발생하는 문제를 피하는가장 쉬운 방법은 복제와 대입이 발생하지 않도록 막는 것이다.

operator=과 복제 생성자를 private로 선언하면 쉽게 막을 수 있다.

Page 17: 2014-15 Intermediate C++ Study #7

여러 종류의 데이터 멤버 변수STATIC 데이터 멤버, CONST 데이터 멤버, 참조형 데이터 멤버, CONST 참조형 데이터 멤버

Page 18: 2014-15 Intermediate C++ Study #7

STATIC 데이터 멤버

static 데이터 멤버는 C에서의 전역 변수와 유사하나 특정 클래스에 종속된다는 점이 다르다.C++11에서는 constexpr을 통해 static 멤버 변수를 선언과 동시에 초기화할 수 있고,

추가적인 작업 없이 바로 static 변수의 사용이 가능하다.

Visual Studio 2013에서는constexpr 키워드를 지원하지 않는다.

(gcc 4.8.1은 지원, –std=c++11 옵션 필요)자세한 사항은 N2756 문서*를 참조

C++11이 지원되지 않는 환경에서는cpp 파일에 sCounter 변수의 메모리를

할당하고 초기화해야 한다.

각 스프레드시트마다 순번을 매기는 변수

* http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm

Page 19: 2014-15 Intermediate C++ Study #7

STATIC 데이터 멤버

메서드 안에서는 static 멤버를 일반 멤버 변수와 같은 방식으로 이용할 수 있다.객체가 생성될 때 복제 생성자가 이용될 수도 있다는 것을 잊지 말자. 따라서 고유 순번 할당 작업을 한다.

대입 연산자는 고유 순번 할당이 필요없다. 고유 순번은 객체 생성 시점에 한 번 할당된 후 변경되지 않아야 한다.

복제 생성자

생성자

Page 20: 2014-15 Intermediate C++ Study #7

CONST 데이터 멤버

클래스의 데이터 멤버를 const로 선언하면, 생성 시점에 초깃값을 부여한 다음 더는 값을 변경할 수 없게 된다.그런데 객체 수준에서 상숫값을 보유하는 것은 대부분 메모리 낭비이다.

이때는 static const 멤버를 이용해서 객체 간에 상숫값을 공유하도록 할 수 있다.

Page 21: 2014-15 Intermediate C++ Study #7

참조형 데이터 멤버

스프레드시트 프로그램을 완성하려면 Spreadsheet, SpreadsheetCell 클래스가SpreadsheetApplication 클래스에 통합되어야 한다. 그런데…

SpreadsheetCell

Spreadsheet

SpreadsheetApplication

Spreadsheet SpreadsheetApplication

SpreadsheetApplication은 복수의 Spreadsheet를 관리하기 때문에Spreadsheet 클래스를 알아야만 한다.

따라서 SpreadsheetApplication을 정의하기 전에 Spreadsheet가 정의되어 있어야 한다.

Spreadsheet 클래스도 SpreadsheetApplication 클래스와커뮤니케이션해야 하기 떄문에 SpreadsheetApplication을 알아야 한다.

따라서 Spreadsheet을 정의하기 전에 SpreadsheetApplication이 정의되어 있어야 한다.

Page 22: 2014-15 Intermediate C++ Study #7

참조형 데이터 멤버

#include로는 해결할 방법이 없다. 이에 대한 해결책으로 포워드 선언(Forward Declaration)이 있다.교차 참조되는 클래스의 헤더 파일 중 어느 한 쪽에 상대편 클래스의 헤더를 인클루드하는 대신

포워드 선언을 해두면 컴파일러가 나중에 해당 정의를 찾아다가 타입 매칭을 한다.

Page 23: 2014-15 Intermediate C++ Study #7

참조형 데이터 멤버

참조형 변수는 생성과 동시에 다른 객체를 참조하도록 초기화해야 한다.한 번 초기화되고 나면 다른 값으로 바꿀 수가 없다.

Page 24: 2014-15 Intermediate C++ Study #7

CONST 참조형 데이터 멤버

const 참조형 변수로 참조된 객체는 const 메서드만 이용할 수 있다.만약 const 참조된 객체에서 const가 아닌 메서드를 호출하면 컴파일 에러가 발생한다.static 참조형 메버나 static const 참조형 멤버도 선언할 수 있지만 특별한 의미는 없다.

Page 25: 2014-15 Intermediate C++ Study #7

메서드의 종류STATIC 메서드, CONST 메서드, 메서드 오버로딩, 디폴트 파라미터, INLINE 메서드

Page 26: 2014-15 Intermediate C++ Study #7

STATIC 메서드

static 메서드의 선언 방법은 static 데이터 멤버와 같다. (특정 클래스의 모든 객체에 공통적으로 적용)static 메서드는 객체에 묶이지 않기 때문에 “객체에 변경을 가하지 않는다.”라는 시맨틱을 적용할 수 없다.

따라서 static 메서드를 선언하려면 const를 빼야 한다.

Page 27: 2014-15 Intermediate C++ Study #7

CONST 메서드

메시지를 const로 선언하면 메서드 호출 때문에 객체의 데이터 값이 바뀌지 않는다는 것을 보증해준다.만약 const로 선언한 메서드 안에서 객체의 데이터 멤버를 변경하려 하면 컴파일러가 오류 메시지를 출력할 수 있다.

const 메서드의 동작 방식은 메서드 내에서 접근하는 모든 데이터 멤버를 const로 취급하는 방법으로 구현된다.

Page 28: 2014-15 Intermediate C++ Study #7

CONST 메서드

const가 아닌 객체에 대해서는 const 여부와 관계없이 모든 메서드를 호출할 수 있다.하지만 const 객체에 대해서는 const 메서드만 호출할 수 있다.

가능하다면 객체를 변경하지 않는 모든 메서드에 const 제한자를 적용해 const 객체를 호출하는 것이 바람직하다.

Page 29: 2014-15 Intermediate C++ Study #7

CONST 메서드

메서드가 객체의 특정 데이터 멤버를 변경하기는 하지만 논리적으로는 const인 경우가 있다.스프레드시트 프로그램의 사용자 이용 성향을 프로파일링 해보기 위해 접근 카운터를 구현했다고 하자.

이 카운터는 객체의 데이터에 변화를 주지 않지만, 컴파일러에서는 객체의 데이터 멤버를 변경하는 행위와 구별이 안된다.mutable 속성을 카운터 멤버 변수에 적용하면 해당 변수의 변경이 메서드의 const에 영향을 미치지 않는 것으로 간주한다.

Page 30: 2014-15 Intermediate C++ Study #7

메서드 오버로딩

앞서 클래스 생성자는 이름이 같더라도 파라미터의 타입이나 개수가 다르다면 여러 개를 정의할 수 있었다.그런데 이러한 정의 방식은 일반 메서드나 함수에도 그대로 적용된다.

이름이 같은 메서드, 함수를 파라미터만 달리하여 정의하는 것을 오버로딩(Overloading)이라 한다.컴파일러가 set() 메서드의 호출을 만나면 인자의 데이터 타입과 개수를 보고 적합한 메서드로 매핑해준다.

이러한 메커니즘을 오버로드 지정(Overload Resolution)이라고 한다.

데이터 타입 : int개수 : 1개

데이터 타입 : string개수 : 1개

Page 31: 2014-15 Intermediate C++ Study #7

메서드 오버로딩

C++에서는 파라미터에 차이가 없이 리턴 타입으만 다른 메서드나 함수에 대해서 오버로딩을 허용하지 않는다.왜냐하면 리턴 타입의 차이만으로는 컴파일러가 오버로드 지정을 지정할 수 없는 상황이 많기 때문이다.

const 제한자에 기반한 오버로딩도 가능하다. 이름과 파라미터가 같은 두 메서드 중 어느 한 쪽만 const라면, 그 메서드를 호출한 객체의 타입이 const냐 아니냐에 맞춰서 컴파일러가 호출될 메서드를 지정할 수 있다.

Page 32: 2014-15 Intermediate C++ Study #7

메서드 오버로딩

C++11에서는 특정 파라미터 타입의 오버로드 메서드가 사용되지 않도록 명시적으로 삭제할 수도 있다.

c.foo(1);

double 타입 값 1.2을정수 1로 타입 캐스팅

Page 33: 2014-15 Intermediate C++ Study #7

디폴트 파라미터

디폴트 파라미터는 함수나 메서드의 원형을 선언할 때 각 파라미터에 디폴트 값을 지정할 수 있다.만약 사용자가 해당 인자를 직접 제공하면 디폴트 값은 무시되며, 공란으로 하면 디폴트 값이 자동으로 적용된다.

디폴트 파라미터는 가장 오른쪽 파라미터부터 시작해 파라미터 건너뜀 없이 연속적으로만 적용할 수 있다.

s1(theApp, 100, 100)

s1(theApp, 5, 100)

s1(theApp, 5, 6)

Page 34: 2014-15 Intermediate C++ Study #7

INLINE 메서드

C++에서는 메서드나 함수를 별도의 분리된 코드 블록으로 호출하는 대신, 호출 지점에 바로 메서드나함수의 바디를 옮겨 넣어 호출 오버헤드를 줄이는 방법을 제공한다.이러한 방법을 인라이닝(Inlining)이라 한다.

inline이 제대로 동작하기 위해서는 해당 메서드, 함수의 바디 내용을 해당 코드의 컴파일 시점에 가지고 있어야만 한다.이 때문에 inline 메서드, 함수는 .cpp 파일이 아닌 .h 헤더 파일에서 정의를 가진다.

Page 35: 2014-15 Intermediate C++ Study #7

INLINE 메서드

C++는 inline 키워드를 사용하지 않고도 메서드를 인라이닝할 수 있는 방법을 제공한다.메서드의 구현부를 클래스 정의 후에 따로 만드는 대신, 안에서 바로 구현부를 정의하면 된다.

다만, inline으로 선언했더라도 실제로 인라이닝될지 안될지는 상황에 따라 다르다.컴파일러는 작은 크기의 메서드, 함수만 인라이닝한다.

만약 적절하지 않은 메서드, 함수를 인라이닝하려 하면 컴파일러가 무시해 버린다.

Page 36: 2014-15 Intermediate C++ Study #7

중첩된 클래스

Page 37: 2014-15 Intermediate C++ Study #7

중첩된 클래스

클래스 정의가 메서드나 데이터 멤버만 담을 수 있는 것은 아니다.클래스 정의에는 struct, typedef, enum도 포함될 수 있다. 단, public으로 선언되어 있어야 한다.

클래스 안에 또 다른 클래스를 정의하는 것도 가능하다.

Page 38: 2014-15 Intermediate C++ Study #7

중첩된 클래스

SpreadsheetCell 클래스를 이용하기 위해서는 스코프 지정 연산자에 Spreadsheet::을 붙여야 한다.이러한 문법은 코드를 지저분하게 만든다. 이 때는 typedef를 이용해 축약해서 사용한다.

Page 39: 2014-15 Intermediate C++ Study #7

클래스 종속 나열형 데이터 타입

Page 40: 2014-15 Intermediate C++ Study #7

클래스 종속 나열형 데이터 타입

상숫값들을 정의해야 한다면 #define 대신 나열형 타입을 이용하는 것이 좋다.#define 상수를 이용하면 색상 변수의 타입을 int로 할 수 밖에 없다.

그러면 색상으로 정의되지 않은 상숫값이 설정될 가능성이 있어 이에 대한 검사를 런타임에 별도로 해야 한다.반면에 나열형 타입을 이용하면 컴파일러가 빌드 타임에 잘못된 상수의 이용을 검사해주어 더 효율적이고 안전하다.

Page 41: 2014-15 Intermediate C++ Study #7

FRIEND 속성

Page 42: 2014-15 Intermediate C++ Study #7

FRIEND 속성

friend 설정을 이용하면 다른 클래스 또는 다른 클래스의 메서드에서private나 protected로 선언된 멤버 및 메서드에 접근할 수 있다.

friend 선언은 권한을 열어줄 클래스에서만 할 수 있다. 즉, 다른 클래스에 대해접근 권한을 요청하는 클래스 또는 메서드 스스로 다른 클래스의 friend 선언을 할 수는 없다.

friend는 남용되기 쉽다. friend를 이용하면 내부 구조를 다른 클래스나 함수에 노출하기 때문에추상화 원칙이 훼손된다. 이 때문에 연산자 오버로딩과 같이 불가피한 상황에서만 이용하는 것이 바람직하다.

Page 43: 2014-15 Intermediate C++ Study #7

연산자 오버로딩SPREADSHEETCELL에 덧셈 기능 구현, 산술 연산자의 오버로딩, 비교 연산자의 오버로딩

Page 44: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

1. add 메서드

Page 45: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

값에 의한 전달 대신, 복제가 일어나지 않는 참조형으로 리턴하면 어떨까 하고 생각할 수도 있다.하지만 add() 메서드가 리턴하는 순간 newCell 객체는 소멸되기 때문에 참조형으로 리턴하게 되면

메서드의 호출이 끝나자마자 댕글링 참조(Dangling Reference)가 되어 버린다.

이 순간, add() 메서드에서 생성했던 newCell 객체는 사라지게 된다.

참조형은 어떤 변수를 가리켜야 되는데 가리킬 변수가 사라지게 되어 유효하지 않게 된다.

Page 46: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

2. operator+의 오버로딩

Page 47: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

operator+ 메서드에서 받는 파라미터 두 개가 항상 같은 데이터 타입일 필요는 없다.SpreadsheetCell 클래스에서 Spreadsheet 클래스를 파라미터로 받는 operator+ 메서드를 정의할 수도 있다.

논리적으로는 말이 안되지만 컴파일에는 아무런 문제가 없다.

C++ 컴파일러가 코드를 파싱하다가 +, -, =과 같은 기호를 만나게 되면 operator+,operator-, operator=를 각각 찾아서 파라미터 타입에 적합한 메서드를 호출하게 된다.(SpreadsheetCell 클래스나 전역 함수 중 두 개의 SpreadsheetCell 타입 파라미터를 받는operator+라는 이름의 메서드 또는 함수가 있는지 찾는다.)

Page 48: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

operator+ 메서드를 정의해 두면 셀뿐만 아니라 string, double, int 타입 데이터도 덧셈을 할 수 있게 된다.그 이유는 컴파일러가 operator+를 처리할 때 적합한 타입으로 변환할 방법이 있는지도 찾기 때문이다.

만약 생성자 중에 부적합한 타입을 인자로 받는 것이 있다면그 생성자를 이용해 적합한 타입의 임시 객체를 자동으로 생성한다.

SpreadsheetCell에 string 타입 파라미터를 받는 생성자가 있는 것을 발견하고SpreadsheetCell의 임시 객체를 만들어 operator+을 실행한다.

SpreadsheetCell에 double 타입 파라미터를 받는 생성자가 있는 것을 발견하고SpreadsheetCell의 임시 객체를 만들어 operator+을 실행한다.

int를 double로 변환

Page 49: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

3. 전역 함수로서의 operator+

묵시적인 변환은 연산자의 좌항이 SpreadsheetCell 객체인 경우에만동작하고 우항에 위치하면 동작하지 않는다.덧셈은 인자의 좌우 위치와 관계없어야 하므로 분명 무언가 잘못되었다.

문제는 SpreadsheetCell의 메서드로서 operator+를 정의하면객체의 우항에 있을 때만 operator+가 호출된다는데 있다.

하지만 전역 함수로서 operator+를 정의하여특정 객체에 종속되지 않도록 하면 문제가 해결된다.

Page 50: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

전역 함수 operator+는 SpreadsheetCell 클래스의 protected 멤버에 접근해야 하기 때문에 friend로 선언되어야 한다.

Page 51: 2014-15 Intermediate C++ Study #7

SPREADSHEETCELL에 덧셈 기능 구현

위 코드는 문제없이 컴파일되고 실행도 된다. 그런데 operator+ 함수가 호출되지 않는다.위 코드는 4.5와 5.5에 대해 보통의 double 타입 덧셈을 먼저 수행하여 결과적으로 10이 된다.그 뒤 컴파일러는 double 타입 파라미터를 받는 생성자를 발견하고 묵시적인 변환을 수행하여

SpreadsheetCell의 임시 객체를 생성한 다음 대입 연산자를 호출한다.

Page 52: 2014-15 Intermediate C++ Study #7

산술 연산자의 오버로딩

C++에서는 오버로딩된 연산자가 일반 상식에 어긋나는지 어떤지 검사하지 않는다.따라서 각 연산 기호가 가진 일반적인 의미에서 그 기능을 쉽게 유추할 수 있도록 구현하는 것이 바람직하다.

또한 C++에서는 연산자의 우선순위를 바꿀 수 없다. 예를 들어 *과 /는 +, -보다 우선해서 실행된다.

0으로 나누기를 방지하는 부분.만약 0으로 나누려 하면 익셉션을 발생시킨다.

Page 53: 2014-15 Intermediate C++ Study #7

산술 연산자의 오버로딩

축약형 산술 연산자의 오버로딩은 기본 연산자의 오버로딩과 두 가지 면에서 다르다.첫 번째 차이점은, 임시 객체를 만들지 않고 좌항 객체를 변경한다는 점이다.

두 번째 차이점은, 변경된 객체에 대한 참조 타입을 결과 리턴 값으로 생성한다는 점이다.

0으로 나누기를 방지하는 부분.만약 0으로 나누려 하면 익셉션을 발생시킨다.

Page 54: 2014-15 Intermediate C++ Study #7

비교 연산자의 오버로딩

>, <, ==과 같은 비교 연산자도 객체간 작업에 활용하기 좋은 연산자들이다.기본 산술 연산자에서와 마찬가지로 이들 연산자도 전역 함수로 만들어

좌항과 우항 어느 쪽 값이든 묵시적인 변환을 할 수 있도록 한다.

Page 55: 2014-15 Intermediate C++ Study #7

안정된 인터페이스 만들기인터페이스 클래스와 구현 클래스의 활용

Page 56: 2014-15 Intermediate C++ Study #7

인터페이스 클래스와 구현 클래스의 활용

클래스를 디자인할 때는 추상화 원칙을 적용하여 구현과 인터페이스를 분리해야 한다.구체적으로는 모든 데이터 멤버들을 protected나 private로 선언하고

외부에서 멤버들을 접근할 필요가 있을 때는 get/set 메서드를 이용한다.하지만 클래스 정의 문법은 public 인터페이스와 private 데이터 멤버 및 메서드를한 클래스에서 정의하도록 하고 있기 때문에 내부 구현이 사용자에게 노출된다.

Page 57: 2014-15 Intermediate C++ Study #7

인터페이스 클래스와 구현 클래스의 활용

해결 방법은 클래스를 만들 때마다 매번 인터페이스 클래스와 구현 클래스를 두 개씩 만드는 것이다.구현 클래스는 원래 만들던 방식의 클래스와 같다. 인터페이스 클래스는 구현 클래스의 public 메서드만을 담는다.

이렇게 클래스를 분리하면 구현 클래스가 어떻게 바뀌든 public 인터페이스 클래스에는 변화가 없으므로컴파일을 다시 해야 할 필요를 줄일 수 있다.

먼저 기존의 Spreadsheet 클래스 이름을 SpreadsheetImpl로 바꾼다.그리고 새로운 Spreadsheet 클래스를 정의한다.

한 가지 다른 점은 Spreadsheet 생성자로 초기화할 const 멤버가없어졌기 때문에 디폴트 파라미터를 설정할 수 없어 생성자가 두 개로 나뉘었다.대신 SpreadsheetImpl 클래스에서 해당 디폴트 값을 설정한다.

Page 58: 2014-15 Intermediate C++ Study #7

인터페이스 클래스와 구현 클래스의 활용

Spreadsheet 메서드의 구현부는 단지 SpreadsheetImpl 객체의 해당 메서드 호출을 중계해준다.이러한 테크닉은 인터페이스와 구현을 완전하게 분리해주며 컴파일 시간을 단축해주기 때문에 매우 강력하다.더 나아가서 프리컴파일된 헤더(Pre-Compiled Header)를 이용해 기본적인 빌드 시간 자체를 줄일 수도 있다.