SlideShare una empresa de Scribd logo
1 de 64
MEC++ chapter1,2
NHN NEXT 장문익
항목1: pointer와 reference 구분
• reference는 null reference가 없다. 항상 메모리 공간을 차지한
객체를 참조해야 한다.
• 그러므로 어떤 객체를 참조하는 변수를 하나 두고 싶은데, 그 변
수가 참조하는 객체가 없을 수도 있는 경우는 pointer를 사용해
야 한다.
• 반대로 객체를 참조하는 어떤 변수가 가리키는 메모리가 항상
유효한 객체면 reference를 쓴다.
reference
• 반드시 객체를 참조하고 있어야 하기 때문에, 선언될 때 초기화
해야 한다.
• 반면에 pointer 초기화되지 않아도 사용할 수 있다.
reference가 효율적일 수 있다.
다른 객체를 참조할 수 있는가?
참조자를 반드시 써야할 특수한 상황
• operator[] 연산자는 대입 연산자의 좌변으로 쓸 수 있는 값을
반환해주는 것이 보통이다.
• 만약 operator[]가 포인터를 반환하면
• 이 코드는 v가 포인터의 벡터로 보이게 하는 단점이 있다.
항목1 정리
• 참조자는 참조하고자 하는 어떤 객체를 미리 알고 있을때, 다른
객체를 바꾸어 참조할 일이 결코 없을 때, 포인터를 사용하면 문
법상 의미가 어색해지는 연산자를 구현할 때 사용한다.
• 이 세 가지의 경우를 제외하고는 무조건 포인터.
항목2: C++ 스타일의 캐스트
• C 스타일의 캐스트는 어떤 타입을 다른 타입으로 아무 생각없
이 막 바꿔주는 괴물이다.
• C 스타일의 캐스트는 눈으로 찾기 힘들다.
• 그래서 C++ 스타일의 캐스트를 쓰자
static_cast
• C 스타일 캐스트와 같은 의미와 형변환 능력을 지니고 있다.
• const를 떼어내지 못한다.(const_cast로 대신한다.)
• const_cast는 const와 volatile를 제거하는 것 이외의 용도는 없
다.
const_cast
dynamic_cast
• 상속 계층 관계를 가로지르거나 하향시킨 클래스 타입으로 안
전하게 캐스팅할 때 사용한다.
• 기본 클래스의 객체에 대한 포인터나 참조자의 타입을 파생 클
래스 혹은 형제 클래스의 타입으로 변환해준다.
• 캐스팅 실패는 널 포인터(포인터를 캐스팅할 때)나 예외(참조자
를 캐스팅할 때)를 보고 판변할 수 있다.
dynamic_cast와 down casting
dynamic_cast의 제약사항
• 상속 계층 구조를 오갈 때에만 사용해야 한다.
• 가상 함수가 없는 타입에는 적용할 수 없다.
• 상수성 제거에도 쓸 수 없다.(const_cast를 사용해야 한다)
reinterpret_cast
• 이 연산자가 적용된 후의 변환 결과는 거의 항상 컴파일러에 따
라 다르게 정의되어 있다.
• 따라서 이 연산자가 쓰인 소스는 직접 이식이 불가능하다.
reinterpret_cast의 제약사항
항목3: 배열과 다형성은 다르다.
• “다형성을 가지고 있다.”: 기본 클래스 객체의 포인터나 참조자
를 통해 파생 클래스 객체를 조작할 수 있다.
배열은 배열의 첫 요소를 가리키는 포인터
• sizeof(BST)와 sizeof(BalancedBST)는 다르다.
• BST를 기준으로 만들어진 포인터값 계산코드가 BalancedBST에
서 제대로 작동할 것이라 확신할 수 없다.
객체 배열이 삭제될 때는 각 객체의 소멸자가 호출
눈에 보이지 않는 포인터값 계산이 배열 삭제에서 이루어 진다.
객체 배열 삭제와 소멸자 호출
항목 4: 쓸데 없는 기본 생성자는 그냥 두지 말자
• 생성자는 객체를 초기화하는 역할을 맡는다.
• 기본 생성자는 객체가 생성되고 있는 위치의 주변 정보를 전혀
갖지 않고도 객체를 초기화한다.
• 외부 정보 없이 객체가 생성될 수 있는 클래스는 기본 생성자를
가져야 할 것이다.
• 개체를 생성되려면 외부 정보를 요구하는 경우의 클래스는 기
본 생성자를 가지면 안 된다.
기본 생성자가 없는 클래스를 사용
기본 생성자가 없는 클래스의 단점1
• 생성자의 매개변수를 직접 배열에 넣어줄 수는 있는데, 번거로
운 일이다.
객체 배열 대신에 포인터 배열로
• 배열 내의 모든 포인터가 가리키는 객체를 삭제해야 한다.
• EquipmentPiece 객체를 담을 메모리 공간 + 포인터를 담을 공
간 때문에 메모리 사용량이 증가
배열에 대해 직접 비가공 메모리를 할당 후 “메모리 지정 new”
“메모리 지정 new”방법의 단점
• 객체를 삭제할 때에 소멸자를 손으로 직접 호출해야 한다.
• operator delete[]를 호출해서 원래의 비가공 메모리를 직접 해
제해야 한다.
기본 생성자가 없는 클래스의 단점2
• 많은 템플릿 기반의 컨테이너 클래스에 먹일(?) 수가 없다.
• 컨테이너 클래스를 인스턴스화하기 위해서는, 템플릿 매개 변수
로 들어가는 타입이 기본 생성자를 가지고 있어야 하기 때문이
다.
• 예는 다음과 같다.
컨테이너 클래스 인스턴스화
template<class T>
class Array {
public:
Array(int size);
...
private:
T *data;
};
template<class T>
Array<T>::Array(int size)
{
data = new T[size]; //
T::T() 호출
...
}
기본 생성자를 제공하느냐 마느냐?
• 기본 생성자가 없는 가상 기본 클래스는 소프트웨어 개발에 쓰
기 힘들다.
• 가상 기본 클래스의 생성자 매개변수를, 생성되는 객체의 파생
클래스 쪽에서 제공해야 하기 때문
• 그러면 모든 클래스에 기본 생성자를 넣어야 하나?
• 기본 생성자로는 객체를 초기화하는데 필요한 정보를 얻기 힘
들어도 기본 생성자를 넣어야 하나?
초기화 정보가 부족한 기본 생성자
class EquipmentPiece {
public:
EquipmentPiece(int IDNumber = UNSPECIFIED);
...
private:
static const int UNSPECIFIED; // ID가 설정되지 않음을 표시하
는 const
};
Equipment e; // OK
위의 코드는 객체의 멤버가 제대로 초기화되었는지 보장할 수 없다.
쓸 데 없는 기본 생성자는 피하자.
• 기본 생성자에게 확실한 초기화를 기대하지 말자.
• 쓸 데 없는 상황에서는 기본 생성자를 피한다.
• 클래스 생성자가 객체의 멤버 데이터를 확실히 초기화한다.
• 이렇게 하면 멤버 함수를 작성할 때 모든 객체 멤버가 제대로
초기화되었다는 확신을 가질 수 있다.
항목 5: 사용자 정의 타입변환 함수를 주
의
• 문제는 C++ 컴파일러가 스스로 알아서 암시적인 타입변환을
하는 데서 시작한다.
컴파일러가 사용하는 타입변환 함수
• single-argument constructor: 인자를 하나만 받아 호출되는 생
성자.
• implicit type conversion operator
single-argument constructor
• 매개변수가 하나를 받도록 선언되어 있든지,
• 매개변수가 여러 개인데 처음 것을 제외한 나머지가 모두 기본
값을 갖도록 되어 있다.
암시적 변환의 예
class Rational { // 유리수를 위한
클래스
public:
Rational(int numerator = 0,
int
denominator = 1);
...
};
class Rational {
public:
...
operator double() const;
}; // double로 암시적으로 바
꾼다.
Rational r(1, 2); // r은 ½
double d = 0.5 * r; // r이 double
로 변환
암시적 변환의 위험성
Rational r(1, 2);
cout << r; // 1/2를 출력해야 한
다.
• operator << 함수를 작성하지
않아도 출력
• 컴파일러는 operator<< 호출 시
점에 이 함수를 못 찾으면, 함수
호출을 성공시키기 위해 암시적
타입변환 함수를 찾는다.
• 결국, Rational::operator double()
를 찾아서 암시적 변환을 수행
• 문제는 r이 유리수가 아니라 부
동 소수점 실수
함수 바꿔치기로 암시적 변환 막기
class Rational {
public:
...
double asDouble() const;
}; // Rational을 double로
바꾼다.
Ration r(1,2);
cout << r;
cout << r.asDouble();
똑같은 일을 하되 다른 이름을
갖는 함수로 연산자를 바꿔치
기 한다.
불편하지만 잘못된 함수 호출
은 방지
암시적 타입변환 연산자
template<class T>
class Array {
public:
Array(int lowBound, int highBound);
Array(int size);
T& operator[](int index);
...
};
bool operator==(const Array<int>& lhs,
const Array<int>& rhs);
Array<int>a(10);
Array<int>b(10);
...
for (int i = 0; i < 10; ++i) {
if (a == b[i]) { // a[i]가 되어야 한다.
...
}
else {
...
}
}
for (int i = 0; i < 10; ++i) {
if (a == b[i]) { // a[i]가
되어야 한다.
...
}
else {
...
}
}
for (int i = 0; i < 10; ++i)
if (a ==
static_case<Array<int> >(b[i])).
..
컴파일러는 좌측의 코드를 이
렇게 바꾼다.
의도하지 않은 변환이다.
암시적 타입변환 연산자 피하기
• 당연한 소리지만 선언하지 않으면 된다.
• 단일 인자 생성자를 안 쓸 수는 없다.
• 그러면 컴파일러가 이런 연산자를 호출하지 않게 하면 될 것 같
다.
explicit
• 암시적 타입변환의 문제를 막기 위해 만들어 졌다.
• 매개변수와 호출이 명확할 때에만 이 생성자를 호출하라는 의
미
• explicit로 선언된 생성자는 암시적 타입변환에 사용되지 않는다.
• “명시적“ 타입변환은 허용
사용자 정의 타입변환 함수는 두 개 이상 쓰이지 않는다.
template<class T>
class Array {
public:
class ArraySize {
public:
ArraySize(int numElements) :
thesize(numElements) {}
int size() const { return thesize; }
private:
int thesize;
};
Array(int lowBound, int highBound);
Array(ArraySize size); // int -> ArraySize
...
};
for (int i = 0; i < 10; ++i) {
if (a == b[i]) … // error
int – ArraySize, ArraySize – Array<int> 두 번의 변환을 거쳐야 하기
때문에 error가 발생한다.
ArraySize처럼 쓰이는 클래스를 proxy class라 한다.
proxy class는 다른 방법으로는 어떻게 할 수 없는 소프트웨어의 동
작방식을 조정하는데 쓰인다.
항목6: prefix / postfix 형태 구분
class UPInt {
public:
UPInt& operator++(); // prefix
const UPInt operator++(int); //
postfix
UPInt& operator--(); //
prefix
const UPInt operator--(int);
// postfix
UPInt& operator += (int);
...
};
UPInt i;
++i; // i.operator++()
i++; // i.operator++(0)
--i; // i.operator--()
i--; // i.operator--(0)
prefix /
• prefix 증가 연산자: 증가시키고 값을 사용하는 연산자
// prefix
UPInt& UPInt::operator++()
{
*this += 1; // 증가
return *this; // 값을 가져온다.
}
postfix
• postfix 증가 연산자: 값을 사용하고 증가시키는 연산자
// postfix
const UPInt UPInt::operator++(int)
{
const UPInt oldValue = *this; // 값을 가져온다.
++(*this); // 증가
return oldValue; // 가져온 값을 반환
}
사용자 정의 타입은 되도록 prefix 증가
연산자
• postfix 증가 연산자는 반환값으로 쓰기 위한 임시 객체를 만들
어야 하고, 지역 변수로서 생겼다가 없어지는 임시객체
(oldValue)를 대놓고 만든다.
• prefix 증가 연산자는 이런 임시 객체를 사용하지 않는다.
• postfix 증감 연산자는 반드시 prefix 증감 연산자를 사용해서 구
현하라.
항목7: &&, ||, . 연산자는 오버로딩 금지
• short-circuit: 복합적인 Boolean 표현식을 평가할 때, 표현식의
일부가 참 혹은 거짓이란 것이 판명되면, 그 이후의 표현식은
pass
short-circuit을 function call로 대체
// operator&& 오버로딩
if (expression1 && expression2) ...
if (expression1.operator&&(expression2))... // operator&&가 멤버함
수
if (opearator&&(expression1, expression2))... // operator&&가
전역함수
function call은 모든 매개변수를 평가한다.(short-circuit을 하지 않는
다.)
매개변수 평가 순서가 정해져 있지 않다.(short-circuit은 좌에서 우로)
쉼표(,) 연산자
void reverse(char s[])
{
for (int i = 0, j = strlen(s) - 1; i < j; ++i, --j) { // 쉼표 연산자
사용
int c = s[i];
s[i] = s[j];
s[j] = c;
}
}
쉼표 연산자는 왼쪽에 있는 표현식을 먼저 평가하고 오른쪽 평가
쉼표 연산자의 결과값은 결국 오른쪽 –j 값이다.
오버로딩할 수 없는 연산자
오버로딩할 수 있는 연산자
항목8: new와 delete의 의미
string *ps = new string(“Memory Management”);
• 여기서 사용된 new는 new 연산자
• new 연산자는 요청한 타입의 객체를 담을 수 있는 크기의 메모
리를 할당하고,
• 그 객체의 생성자를 호출하여 할당된 메모리에 객체 초기화를
수행한다.
operator new
• new 연산자는 필수적인 메모리 할당을 위해 어떤 함수를 호출하는
데, 이 함수가 operator new이다.
• 이 함수는 다시 작성하든지 오버로딩할 수 있다.
void * operator new(size_t size);
이 함수는 초기화되지 않은 원시 메모리의 포인터를 반환하기 때문에
void*
size_t 매개변수는 할당할 메모리의 크기를 지정
operator new를 오버로딩할 때에는 size_t 이외의 변수를 추가할 수
있는데, 첫째 매개변수는 항상 size_t여야 한다.
new 연산자 작동 방식
• string *ps = new string("Memory Management");
void *memory = operator new(sizeof(string));
*memory에 대해 string::string("Memory Management")를 호출
한다.(컴파일러 담당)
string *ps = static_cast<string*>(memory);
메모리 지정 new(Placement new)
• 할당받은 미초기화 메모리가 있는 경우, 이를 객체처럼 사용하
고자 할 때, operator new의 특별판인 메모리 지정
new(placement new)를 쓴다.
• #include <new> 혹은 #include <new.h>가 필요하다.
메모리 지정 new(Placement new)
class Widget {
public:
Widget(int widgetSize);
...
};
Widget * constuctWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer)Widget(widgetSize); // new 연산자가 operator new를
호출하면서
// 매개변
수 buffer를 더 추가
}
메모리 지정 new(Placement new)
void * operator new(size_t, void *location)
{
return location;
}
앞 페이지의 코드는 위와 같이 operator new를 오버로딩한 것이
다.
어쨌든 operator new의 역할은 객체를 만들 메모리를 찾아 그 메
모리의 포인터를 반환하는 것이다.
deletion & deallocation
• 리소스 누수를 막기 위해서는, 할당한 메모리는 반드시 그에 대
응되는 메모리 해제를 통해 운영체제에게 돌려주어야 한다.
• C++에서는 delete 연산자를 사용
• new 연산자와 마찬가지로 내부적인 함수를 사용하는데, 이 함
수가 operator delete
delete 연산자 작동 방식
string *ps;
…
delete ps; // delete 연산자
컴파일러는 ps가 가리키는 객체에 대한 소멸자를 호출하고 그 객
체가 자리잡은 메모리를 해제하는 코드를 만들어 낸다.
operator delete
void operator delete(void *memoryToBeDeallocation);
delete ps;
컴파일러는 이 문장을 보고 아래와 같은 코드를 만든다.
ps->~string(); // 객체 소멸자 호출
operator delete(ps); // 객체가 자리잡은 메모리를 해제
한다.
미초기화 메모리 사용
• 미초기화 메모리만 가지고 일을 할 때에는 new와 delete 연산자가
아닌 operator new와 operator delete를 호출해야 한다.
// 50개 문자를 담을 수 있는 메모리할당. 생성자 호출하지 않음
void *buffer = operator new(50 * sizeof(char));
// 메모리 해제. 소멸자 호출하지 않음
operator delete(buffer);
C++ 버전의 malloc과 free이다.
메모리 지정 new는 소멸자를 직접 호출
하라
void *mallocShared(size_t size);
void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = constuctWidgetInBuffer(sharedMemory, 10); // 메모리 지정 new
delete pw; // 결과 예측 불능. sharedMemory는 mallocShared이다.
pw->~Widget(); // 문제 없음. pw가 가리킨 Widget 객체의 소멸자 호출. 메모
리 해제는 않함
freeShared(pw); // 문제 없음. pw가 가리킨 메모리를 해제하지만, 소멸자 호
출 않함.
배열(Arrays)
string *ps = new string[10] // 객체의 배열을 할당
여기에 사용된 new는 new 연산자이다.
하지만 배열 생성에 사용되는 new 연산자는 단일객체 생성에 사
용되는 그것과는 다르다.
배열생성 new 연산자
• 메모리를 할당할 때 operator new 함수를 호출하지 않는다.
• operator new의 배열 할당용 버전인 operator new[]를 호출한
다.(array new라고들 부른다)
• 호출하는 생성자의 개수도 단일 객체 생성용 new와 다르다.
• operator new[]는 배열 요소에 대해 일일이 생성자를 호출해야
한다.
• operator new도 오버로딩이 가능하다.
operator delete []
strind *ps = new string[10]; // operator new[] 호출하여 10개의
string 객체에 대한 메모리 할당
// 배열의 각 요소에 대
해 기본 생성자 호출
delete [] ps; // 배열의 각 요소에 대
해 소멸자 호출
// operator delete[]를
써서 배열 전체 메모리를 해제

Más contenido relacionado

La actualidad más candente

Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
현찬 양
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2
문익 장
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2
현찬 양
 

La actualidad más candente (20)

Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
 
C++ Advanced 강의 4주차
 C++ Advanced 강의 4주차 C++ Advanced 강의 4주차
C++ Advanced 강의 4주차
 
effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리
 
Effective c++chapter3
Effective c++chapter3Effective c++chapter3
Effective c++chapter3
 
Effective c++ 정리 1~2
Effective c++ 정리 1~2Effective c++ 정리 1~2
Effective c++ 정리 1~2
 
Effective c++chapter8
Effective c++chapter8Effective c++chapter8
Effective c++chapter8
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2
 
Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
Effective c++chapter1 and2
Effective c++chapter1 and2Effective c++chapter1 and2
Effective c++chapter1 and2
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshin
 
Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8Effective c++ 정리 chapter 8
Effective c++ 정리 chapter 8
 
파이썬 sqlite 이해하기
파이썬 sqlite 이해하기파이썬 sqlite 이해하기
파이썬 sqlite 이해하기
 
이것이 자바다 Chap. 6 클래스(CLASS)(KOR)
이것이 자바다 Chap. 6 클래스(CLASS)(KOR)이것이 자바다 Chap. 6 클래스(CLASS)(KOR)
이것이 자바다 Chap. 6 클래스(CLASS)(KOR)
 
Api design for c++ pattern
Api design for c++ patternApi design for c++ pattern
Api design for c++ pattern
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2
 
C++ Advanced 강의 2주차
C++ Advanced 강의 2주차C++ Advanced 강의 2주차
C++ Advanced 강의 2주차
 
8 swift 중첩함수
8 swift 중첩함수8 swift 중첩함수
8 swift 중첩함수
 
Effective C++ Chaper 1
Effective C++ Chaper 1Effective C++ Chaper 1
Effective C++ Chaper 1
 
Javascript - Function
Javascript - FunctionJavascript - Function
Javascript - Function
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉
 

Similar a More effective c++ chapter1,2

Effective c++ chapter 7,8
Effective c++ chapter 7,8Effective c++ chapter 7,8
Effective c++ chapter 7,8
문익 장
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 pattern
jinho park
 
M1 2 1
M1 2 1M1 2 1
M1 2 1
nexthw
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4
문익 장
 
Ec++ 3,4 summary
Ec++ 3,4 summaryEc++ 3,4 summary
Ec++ 3,4 summary
Sehyeon Nam
 
Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4
연우 김
 
파이썬 스터디 9장
파이썬 스터디 9장파이썬 스터디 9장
파이썬 스터디 9장
SeongHyun Ahn
 
More effective c++ 3
More effective c++ 3More effective c++ 3
More effective c++ 3
현찬 양
 

Similar a More effective c++ chapter1,2 (20)

Effective c++ 1~8장
Effective c++ 1~8장 Effective c++ 1~8장
Effective c++ 1~8장
 
Effective c++ chapter 7,8
Effective c++ chapter 7,8Effective c++ chapter 7,8
Effective c++ chapter 7,8
 
2014-15 Intermediate C++ Study #6
2014-15 Intermediate C++ Study #62014-15 Intermediate C++ Study #6
2014-15 Intermediate C++ Study #6
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 pattern
 
M1 2 1
M1 2 1M1 2 1
M1 2 1
 
Chapter7~9 ppt
Chapter7~9 pptChapter7~9 ppt
Chapter7~9 ppt
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
 
Effective STL 1~4장 정리
Effective STL 1~4장 정리Effective STL 1~4장 정리
Effective STL 1~4장 정리
 
MEC++ 1, 2
MEC++ 1, 2MEC++ 1, 2
MEC++ 1, 2
 
모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디모어 이펙티브 c++ 5장 스터디
모어 이펙티브 c++ 5장 스터디
 
Mec++ chapter3,4
Mec++ chapter3,4Mec++ chapter3,4
Mec++ chapter3,4
 
Ec++ 3,4 summary
Ec++ 3,4 summaryEc++ 3,4 summary
Ec++ 3,4 summary
 
2014-15 Intermediate C++ Study #7
2014-15 Intermediate C++ Study #72014-15 Intermediate C++ Study #7
2014-15 Intermediate C++ Study #7
 
C++ Template/STL study
C++ Template/STL studyC++ Template/STL study
C++ Template/STL study
 
Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4
 
2013 C++ Study For Students #1
2013 C++ Study For Students #12013 C++ Study For Students #1
2013 C++ Study For Students #1
 
More effective c++ Chap1~2
More effective c++ Chap1~2More effective c++ Chap1~2
More effective c++ Chap1~2
 
파이썬 스터디 9장
파이썬 스터디 9장파이썬 스터디 9장
파이썬 스터디 9장
 
MEC++ 3,4
MEC++ 3,4MEC++ 3,4
MEC++ 3,4
 
More effective c++ 3
More effective c++ 3More effective c++ 3
More effective c++ 3
 

Más de 문익 장 (12)

Mec chapter 5,6
Mec chapter 5,6Mec chapter 5,6
Mec chapter 5,6
 
Std bind
Std bindStd bind
Std bind
 
Winsock
WinsockWinsock
Winsock
 
C++ align
C++ alignC++ align
C++ align
 
Alignment
AlignmentAlignment
Alignment
 
Tcp server / client
Tcp server / clientTcp server / client
Tcp server / client
 
Effective c++(chapter 5,6)
Effective c++(chapter 5,6)Effective c++(chapter 5,6)
Effective c++(chapter 5,6)
 
삼각형으로 지면 만들기
삼각형으로 지면 만들기삼각형으로 지면 만들기
삼각형으로 지면 만들기
 
소켓 주소 구조체 다루기(윈도우 네트워크 프로그래밍)
소켓 주소 구조체 다루기(윈도우 네트워크 프로그래밍)소켓 주소 구조체 다루기(윈도우 네트워크 프로그래밍)
소켓 주소 구조체 다루기(윈도우 네트워크 프로그래밍)
 
3D Graphics Transform
3D Graphics Transform3D Graphics Transform
3D Graphics Transform
 
Direct3d overview
Direct3d overviewDirect3d overview
Direct3d overview
 
Effective c++(chapter3,4)
Effective c++(chapter3,4)Effective c++(chapter3,4)
Effective c++(chapter3,4)
 

More effective c++ chapter1,2

  • 2. 항목1: pointer와 reference 구분 • reference는 null reference가 없다. 항상 메모리 공간을 차지한 객체를 참조해야 한다. • 그러므로 어떤 객체를 참조하는 변수를 하나 두고 싶은데, 그 변 수가 참조하는 객체가 없을 수도 있는 경우는 pointer를 사용해 야 한다. • 반대로 객체를 참조하는 어떤 변수가 가리키는 메모리가 항상 유효한 객체면 reference를 쓴다.
  • 3. reference • 반드시 객체를 참조하고 있어야 하기 때문에, 선언될 때 초기화 해야 한다. • 반면에 pointer 초기화되지 않아도 사용할 수 있다.
  • 5. 다른 객체를 참조할 수 있는가?
  • 6. 참조자를 반드시 써야할 특수한 상황 • operator[] 연산자는 대입 연산자의 좌변으로 쓸 수 있는 값을 반환해주는 것이 보통이다. • 만약 operator[]가 포인터를 반환하면 • 이 코드는 v가 포인터의 벡터로 보이게 하는 단점이 있다.
  • 7. 항목1 정리 • 참조자는 참조하고자 하는 어떤 객체를 미리 알고 있을때, 다른 객체를 바꾸어 참조할 일이 결코 없을 때, 포인터를 사용하면 문 법상 의미가 어색해지는 연산자를 구현할 때 사용한다. • 이 세 가지의 경우를 제외하고는 무조건 포인터.
  • 8. 항목2: C++ 스타일의 캐스트 • C 스타일의 캐스트는 어떤 타입을 다른 타입으로 아무 생각없 이 막 바꿔주는 괴물이다. • C 스타일의 캐스트는 눈으로 찾기 힘들다. • 그래서 C++ 스타일의 캐스트를 쓰자
  • 9. static_cast • C 스타일 캐스트와 같은 의미와 형변환 능력을 지니고 있다. • const를 떼어내지 못한다.(const_cast로 대신한다.) • const_cast는 const와 volatile를 제거하는 것 이외의 용도는 없 다.
  • 11. dynamic_cast • 상속 계층 관계를 가로지르거나 하향시킨 클래스 타입으로 안 전하게 캐스팅할 때 사용한다. • 기본 클래스의 객체에 대한 포인터나 참조자의 타입을 파생 클 래스 혹은 형제 클래스의 타입으로 변환해준다. • 캐스팅 실패는 널 포인터(포인터를 캐스팅할 때)나 예외(참조자 를 캐스팅할 때)를 보고 판변할 수 있다.
  • 13. dynamic_cast의 제약사항 • 상속 계층 구조를 오갈 때에만 사용해야 한다. • 가상 함수가 없는 타입에는 적용할 수 없다. • 상수성 제거에도 쓸 수 없다.(const_cast를 사용해야 한다)
  • 14. reinterpret_cast • 이 연산자가 적용된 후의 변환 결과는 거의 항상 컴파일러에 따 라 다르게 정의되어 있다. • 따라서 이 연산자가 쓰인 소스는 직접 이식이 불가능하다.
  • 16. 항목3: 배열과 다형성은 다르다. • “다형성을 가지고 있다.”: 기본 클래스 객체의 포인터나 참조자 를 통해 파생 클래스 객체를 조작할 수 있다.
  • 17. 배열은 배열의 첫 요소를 가리키는 포인터 • sizeof(BST)와 sizeof(BalancedBST)는 다르다. • BST를 기준으로 만들어진 포인터값 계산코드가 BalancedBST에 서 제대로 작동할 것이라 확신할 수 없다.
  • 18. 객체 배열이 삭제될 때는 각 객체의 소멸자가 호출 눈에 보이지 않는 포인터값 계산이 배열 삭제에서 이루어 진다.
  • 19. 객체 배열 삭제와 소멸자 호출
  • 20. 항목 4: 쓸데 없는 기본 생성자는 그냥 두지 말자 • 생성자는 객체를 초기화하는 역할을 맡는다. • 기본 생성자는 객체가 생성되고 있는 위치의 주변 정보를 전혀 갖지 않고도 객체를 초기화한다. • 외부 정보 없이 객체가 생성될 수 있는 클래스는 기본 생성자를 가져야 할 것이다. • 개체를 생성되려면 외부 정보를 요구하는 경우의 클래스는 기 본 생성자를 가지면 안 된다.
  • 21. 기본 생성자가 없는 클래스를 사용
  • 22. 기본 생성자가 없는 클래스의 단점1 • 생성자의 매개변수를 직접 배열에 넣어줄 수는 있는데, 번거로 운 일이다.
  • 23. 객체 배열 대신에 포인터 배열로 • 배열 내의 모든 포인터가 가리키는 객체를 삭제해야 한다. • EquipmentPiece 객체를 담을 메모리 공간 + 포인터를 담을 공 간 때문에 메모리 사용량이 증가
  • 24. 배열에 대해 직접 비가공 메모리를 할당 후 “메모리 지정 new”
  • 25. “메모리 지정 new”방법의 단점 • 객체를 삭제할 때에 소멸자를 손으로 직접 호출해야 한다. • operator delete[]를 호출해서 원래의 비가공 메모리를 직접 해 제해야 한다.
  • 26. 기본 생성자가 없는 클래스의 단점2 • 많은 템플릿 기반의 컨테이너 클래스에 먹일(?) 수가 없다. • 컨테이너 클래스를 인스턴스화하기 위해서는, 템플릿 매개 변수 로 들어가는 타입이 기본 생성자를 가지고 있어야 하기 때문이 다. • 예는 다음과 같다.
  • 27. 컨테이너 클래스 인스턴스화 template<class T> class Array { public: Array(int size); ... private: T *data; }; template<class T> Array<T>::Array(int size) { data = new T[size]; // T::T() 호출 ... }
  • 28. 기본 생성자를 제공하느냐 마느냐? • 기본 생성자가 없는 가상 기본 클래스는 소프트웨어 개발에 쓰 기 힘들다. • 가상 기본 클래스의 생성자 매개변수를, 생성되는 객체의 파생 클래스 쪽에서 제공해야 하기 때문 • 그러면 모든 클래스에 기본 생성자를 넣어야 하나? • 기본 생성자로는 객체를 초기화하는데 필요한 정보를 얻기 힘 들어도 기본 생성자를 넣어야 하나?
  • 29. 초기화 정보가 부족한 기본 생성자 class EquipmentPiece { public: EquipmentPiece(int IDNumber = UNSPECIFIED); ... private: static const int UNSPECIFIED; // ID가 설정되지 않음을 표시하 는 const }; Equipment e; // OK 위의 코드는 객체의 멤버가 제대로 초기화되었는지 보장할 수 없다.
  • 30. 쓸 데 없는 기본 생성자는 피하자. • 기본 생성자에게 확실한 초기화를 기대하지 말자. • 쓸 데 없는 상황에서는 기본 생성자를 피한다. • 클래스 생성자가 객체의 멤버 데이터를 확실히 초기화한다. • 이렇게 하면 멤버 함수를 작성할 때 모든 객체 멤버가 제대로 초기화되었다는 확신을 가질 수 있다.
  • 31. 항목 5: 사용자 정의 타입변환 함수를 주 의 • 문제는 C++ 컴파일러가 스스로 알아서 암시적인 타입변환을 하는 데서 시작한다.
  • 32. 컴파일러가 사용하는 타입변환 함수 • single-argument constructor: 인자를 하나만 받아 호출되는 생 성자. • implicit type conversion operator
  • 33. single-argument constructor • 매개변수가 하나를 받도록 선언되어 있든지, • 매개변수가 여러 개인데 처음 것을 제외한 나머지가 모두 기본 값을 갖도록 되어 있다.
  • 34. 암시적 변환의 예 class Rational { // 유리수를 위한 클래스 public: Rational(int numerator = 0, int denominator = 1); ... }; class Rational { public: ... operator double() const; }; // double로 암시적으로 바 꾼다. Rational r(1, 2); // r은 ½ double d = 0.5 * r; // r이 double 로 변환
  • 35. 암시적 변환의 위험성 Rational r(1, 2); cout << r; // 1/2를 출력해야 한 다. • operator << 함수를 작성하지 않아도 출력 • 컴파일러는 operator<< 호출 시 점에 이 함수를 못 찾으면, 함수 호출을 성공시키기 위해 암시적 타입변환 함수를 찾는다. • 결국, Rational::operator double() 를 찾아서 암시적 변환을 수행 • 문제는 r이 유리수가 아니라 부 동 소수점 실수
  • 36. 함수 바꿔치기로 암시적 변환 막기 class Rational { public: ... double asDouble() const; }; // Rational을 double로 바꾼다. Ration r(1,2); cout << r; cout << r.asDouble(); 똑같은 일을 하되 다른 이름을 갖는 함수로 연산자를 바꿔치 기 한다. 불편하지만 잘못된 함수 호출 은 방지
  • 37. 암시적 타입변환 연산자 template<class T> class Array { public: Array(int lowBound, int highBound); Array(int size); T& operator[](int index); ... }; bool operator==(const Array<int>& lhs, const Array<int>& rhs); Array<int>a(10); Array<int>b(10); ... for (int i = 0; i < 10; ++i) { if (a == b[i]) { // a[i]가 되어야 한다. ... } else { ... } }
  • 38. for (int i = 0; i < 10; ++i) { if (a == b[i]) { // a[i]가 되어야 한다. ... } else { ... } } for (int i = 0; i < 10; ++i) if (a == static_case<Array<int> >(b[i])). .. 컴파일러는 좌측의 코드를 이 렇게 바꾼다. 의도하지 않은 변환이다.
  • 39. 암시적 타입변환 연산자 피하기 • 당연한 소리지만 선언하지 않으면 된다. • 단일 인자 생성자를 안 쓸 수는 없다. • 그러면 컴파일러가 이런 연산자를 호출하지 않게 하면 될 것 같 다.
  • 40. explicit • 암시적 타입변환의 문제를 막기 위해 만들어 졌다. • 매개변수와 호출이 명확할 때에만 이 생성자를 호출하라는 의 미 • explicit로 선언된 생성자는 암시적 타입변환에 사용되지 않는다. • “명시적“ 타입변환은 허용
  • 41. 사용자 정의 타입변환 함수는 두 개 이상 쓰이지 않는다. template<class T> class Array { public: class ArraySize { public: ArraySize(int numElements) : thesize(numElements) {} int size() const { return thesize; } private: int thesize; }; Array(int lowBound, int highBound); Array(ArraySize size); // int -> ArraySize ... }; for (int i = 0; i < 10; ++i) { if (a == b[i]) … // error int – ArraySize, ArraySize – Array<int> 두 번의 변환을 거쳐야 하기 때문에 error가 발생한다. ArraySize처럼 쓰이는 클래스를 proxy class라 한다. proxy class는 다른 방법으로는 어떻게 할 수 없는 소프트웨어의 동 작방식을 조정하는데 쓰인다.
  • 42. 항목6: prefix / postfix 형태 구분 class UPInt { public: UPInt& operator++(); // prefix const UPInt operator++(int); // postfix UPInt& operator--(); // prefix const UPInt operator--(int); // postfix UPInt& operator += (int); ... }; UPInt i; ++i; // i.operator++() i++; // i.operator++(0) --i; // i.operator--() i--; // i.operator--(0)
  • 43. prefix / • prefix 증가 연산자: 증가시키고 값을 사용하는 연산자 // prefix UPInt& UPInt::operator++() { *this += 1; // 증가 return *this; // 값을 가져온다. }
  • 44. postfix • postfix 증가 연산자: 값을 사용하고 증가시키는 연산자 // postfix const UPInt UPInt::operator++(int) { const UPInt oldValue = *this; // 값을 가져온다. ++(*this); // 증가 return oldValue; // 가져온 값을 반환 }
  • 45. 사용자 정의 타입은 되도록 prefix 증가 연산자 • postfix 증가 연산자는 반환값으로 쓰기 위한 임시 객체를 만들 어야 하고, 지역 변수로서 생겼다가 없어지는 임시객체 (oldValue)를 대놓고 만든다. • prefix 증가 연산자는 이런 임시 객체를 사용하지 않는다. • postfix 증감 연산자는 반드시 prefix 증감 연산자를 사용해서 구 현하라.
  • 46. 항목7: &&, ||, . 연산자는 오버로딩 금지 • short-circuit: 복합적인 Boolean 표현식을 평가할 때, 표현식의 일부가 참 혹은 거짓이란 것이 판명되면, 그 이후의 표현식은 pass
  • 47. short-circuit을 function call로 대체 // operator&& 오버로딩 if (expression1 && expression2) ... if (expression1.operator&&(expression2))... // operator&&가 멤버함 수 if (opearator&&(expression1, expression2))... // operator&&가 전역함수 function call은 모든 매개변수를 평가한다.(short-circuit을 하지 않는 다.) 매개변수 평가 순서가 정해져 있지 않다.(short-circuit은 좌에서 우로)
  • 48. 쉼표(,) 연산자 void reverse(char s[]) { for (int i = 0, j = strlen(s) - 1; i < j; ++i, --j) { // 쉼표 연산자 사용 int c = s[i]; s[i] = s[j]; s[j] = c; } } 쉼표 연산자는 왼쪽에 있는 표현식을 먼저 평가하고 오른쪽 평가 쉼표 연산자의 결과값은 결국 오른쪽 –j 값이다.
  • 51. 항목8: new와 delete의 의미 string *ps = new string(“Memory Management”); • 여기서 사용된 new는 new 연산자 • new 연산자는 요청한 타입의 객체를 담을 수 있는 크기의 메모 리를 할당하고, • 그 객체의 생성자를 호출하여 할당된 메모리에 객체 초기화를 수행한다.
  • 52. operator new • new 연산자는 필수적인 메모리 할당을 위해 어떤 함수를 호출하는 데, 이 함수가 operator new이다. • 이 함수는 다시 작성하든지 오버로딩할 수 있다. void * operator new(size_t size); 이 함수는 초기화되지 않은 원시 메모리의 포인터를 반환하기 때문에 void* size_t 매개변수는 할당할 메모리의 크기를 지정 operator new를 오버로딩할 때에는 size_t 이외의 변수를 추가할 수 있는데, 첫째 매개변수는 항상 size_t여야 한다.
  • 53. new 연산자 작동 방식 • string *ps = new string("Memory Management"); void *memory = operator new(sizeof(string)); *memory에 대해 string::string("Memory Management")를 호출 한다.(컴파일러 담당) string *ps = static_cast<string*>(memory);
  • 54. 메모리 지정 new(Placement new) • 할당받은 미초기화 메모리가 있는 경우, 이를 객체처럼 사용하 고자 할 때, operator new의 특별판인 메모리 지정 new(placement new)를 쓴다. • #include <new> 혹은 #include <new.h>가 필요하다.
  • 55. 메모리 지정 new(Placement new) class Widget { public: Widget(int widgetSize); ... }; Widget * constuctWidgetInBuffer(void *buffer, int widgetSize) { return new (buffer)Widget(widgetSize); // new 연산자가 operator new를 호출하면서 // 매개변 수 buffer를 더 추가 }
  • 56. 메모리 지정 new(Placement new) void * operator new(size_t, void *location) { return location; } 앞 페이지의 코드는 위와 같이 operator new를 오버로딩한 것이 다. 어쨌든 operator new의 역할은 객체를 만들 메모리를 찾아 그 메 모리의 포인터를 반환하는 것이다.
  • 57. deletion & deallocation • 리소스 누수를 막기 위해서는, 할당한 메모리는 반드시 그에 대 응되는 메모리 해제를 통해 운영체제에게 돌려주어야 한다. • C++에서는 delete 연산자를 사용 • new 연산자와 마찬가지로 내부적인 함수를 사용하는데, 이 함 수가 operator delete
  • 58. delete 연산자 작동 방식 string *ps; … delete ps; // delete 연산자 컴파일러는 ps가 가리키는 객체에 대한 소멸자를 호출하고 그 객 체가 자리잡은 메모리를 해제하는 코드를 만들어 낸다.
  • 59. operator delete void operator delete(void *memoryToBeDeallocation); delete ps; 컴파일러는 이 문장을 보고 아래와 같은 코드를 만든다. ps->~string(); // 객체 소멸자 호출 operator delete(ps); // 객체가 자리잡은 메모리를 해제 한다.
  • 60. 미초기화 메모리 사용 • 미초기화 메모리만 가지고 일을 할 때에는 new와 delete 연산자가 아닌 operator new와 operator delete를 호출해야 한다. // 50개 문자를 담을 수 있는 메모리할당. 생성자 호출하지 않음 void *buffer = operator new(50 * sizeof(char)); // 메모리 해제. 소멸자 호출하지 않음 operator delete(buffer); C++ 버전의 malloc과 free이다.
  • 61. 메모리 지정 new는 소멸자를 직접 호출 하라 void *mallocShared(size_t size); void freeShared(void *memory); void *sharedMemory = mallocShared(sizeof(Widget)); Widget *pw = constuctWidgetInBuffer(sharedMemory, 10); // 메모리 지정 new delete pw; // 결과 예측 불능. sharedMemory는 mallocShared이다. pw->~Widget(); // 문제 없음. pw가 가리킨 Widget 객체의 소멸자 호출. 메모 리 해제는 않함 freeShared(pw); // 문제 없음. pw가 가리킨 메모리를 해제하지만, 소멸자 호 출 않함.
  • 62. 배열(Arrays) string *ps = new string[10] // 객체의 배열을 할당 여기에 사용된 new는 new 연산자이다. 하지만 배열 생성에 사용되는 new 연산자는 단일객체 생성에 사 용되는 그것과는 다르다.
  • 63. 배열생성 new 연산자 • 메모리를 할당할 때 operator new 함수를 호출하지 않는다. • operator new의 배열 할당용 버전인 operator new[]를 호출한 다.(array new라고들 부른다) • 호출하는 생성자의 개수도 단일 객체 생성용 new와 다르다. • operator new[]는 배열 요소에 대해 일일이 생성자를 호출해야 한다. • operator new도 오버로딩이 가능하다.
  • 64. operator delete [] strind *ps = new string[10]; // operator new[] 호출하여 10개의 string 객체에 대한 메모리 할당 // 배열의 각 요소에 대 해 기본 생성자 호출 delete [] ps; // 배열의 각 요소에 대 해 소멸자 호출 // operator delete[]를 써서 배열 전체 메모리를 해제