operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자.


class widget{ ... }


widget w;

w=w;


widget w[10];

w[i] = w[j];


*pw1 = *pw2;

위의 경우 자기대입의 소지가 다분하다.


같은 타입으로 만들어진 객체 여러 개를

참조자 혹은 포인터로 물어 놓고 동작하는

코드를 작성할 때는 같은 객체가 사용 될

가능성을 고려하는 것이 바람직하다.




파생 클래스 타입의 객체를 참조하거나 가리키는 용도로

기본 클래스의 참조자나 포인터를 사용할 수도 있다.


class Base { ... };

class Derived : public Base { ... }

void dosomethig ( const Base& rb, Derived* pd );

위의 경우에도 자기대입 연산의 위험이 다분하다.


일반적인 operator= 구현을 보겠다.

위 코드의 문제점은 *this와 rhs가

같은 객체일 경우 문제점이 발생한다.

첫 줄에서 이미 지워진 메모리 영역을

두번 째 줄에서 다시 가리키고 있다.

또한 예외에도 안전하지 않다는 점.




자 이제 이 두문제를 해결해보자.


1. 일치성 검사


위의 operator= 구현 맨 윗줄에

다음과 같은 코드를 삽입합니다.

if(this == &rhs) return *this;

자기 대입인지 확인한 후, 자기 대입이면

아무것도 하지 않고 자기 자신을 리턴한다.




자기 대입의 위험성은 이제 완전히

사라진 듯 보이지만 여전히

예외에 안전하지 않다는 문제점이 있다.

또한 대입을 할 때마다 검사를

해줘야 한다는 비용문제도 생긴다.




'new bitmap' 이 부분에서

동적 할당에 필요한 메모리가 부족하다든지

bitmap 클래스 복사 생성자에서 예외를

던진다든지 해서 메모리 할당이 제대로

진행되지 않을 가능성이 있다.


2. 문장의 순서를 바꿔보자


'new bitmap' 부분에서 예외가 발생하더라도

pb는 원래의 데이터를 가지고 있다.

'new' 연산이 정상적으로 수행되면

그 때 pOrig를 삭제한다.

(pOrig는 pb의 원래 가리키던 곳을 가리키고 있다.)




위 코드는 자기대입 현상 또한 완벽히 처리한다.


그림이 좀 엉성하지만

코드를 보고 조금만 생각하면

충분히 이해가 될 것이다.




예외 안전성과 자기대입 안전성을 동시에

가진 operator=을 구현하는 방법으로

복사 후 맞바꾸기라는 기법이 있는데

이 기법은 책의 중반부에 자세히

설명하고 있기 때문에

다음 포스팅에서 다루도록 하겠다.


POINT!!

1. operator=을 구현할 때, 어떤 객체가 그 자신에

대입되는  경우를 제대로 처리하도록 만들어야 한다.


1) 일치성 검사

2) 문장의 순서 조정

3) 복사 후 맞바꾸기


2. 두 개 이상의 객체에 대해 동작하는 함수가 있다면,

이 함수에 넘겨지는 객체들이 같은 경우에도

정확하게 동작하는지 확인해 봐야한다.


함수 포인터와 void 포인터


함수포인터와 void 포인터

이것들이 도대체 무엇인지에 대해

정말 간만보는 포스팅을 진행하겠다.




함수는 CPU에 의해서 실행된다.

CPU에 의해 실행되려면 메모리에 올라와 있어야한다.

(메인 메모리)




함수의 몸체가 메모리 상에 올라가고

그 놈이 어딨는지를 알아야 한다.

고로 함수의 이름이 함수의 몸체가 

메모리의 어디에 있는지 가리키는 포인터가 된다.


void fct(int n, int n2){

..

..

}




함수 포인터의 구성 = 몸체의 주소 + 자료형


함수의 포인터 타입을 결정짓는 요소

1. 리턴 타입

2. 매개 변수 타입




함수 포인터의 선언

void (*pfct)(int n, int n2) = fct;

리턴 타입과 매개 변수의 타입이 같다!

fct는 포인터이기 때문에 선언된 함수포인터에

자신이 가리키는 함수의 주소값을 넘겨줌.


int n = 10;

int * p = &n;

int *fp = p;

위와 같은 형태이다.


사용법 또한 같다

pfct(1,2);

fct(2,4);


pfct = fct ==> 주소값도 같다




void형 포인터

포인터는 주소값을 저장할 수 있는 변수

하지만 자료형에 대한 정보가 제외된 형태.


void* p;

 int, char 구분 없이 저장 가능하다.


int n = 10;

char c = 'c';

void* vp;

vp = &n;

vp = &c;


가능한 이유?

자료형에 대한 정보가 제외되어 있기 때문.


하지만 기능이 제한된다.

포인터 연산, 메모리 참조와 관련된 일에 활용 할 수 없다.


왜?

포인터 연산을 했을 때 메모리를 얼마나 참조할 것인가에 대한 정보 --> 자료형

void 포인터는 어떠한 데이터 특성을 지니는지의 정보가 없다

메모리를 어떻게 참조할지 모른다.(타입에 대한 정보가 없기 때문)


int main(){

int n = 10;

void * vp = &n;

*vp = 20;

 --> 1바이트를 참조할지 4바이트를 참조할지 모른다.

vp++;

--> 이 또한 마찬가지

}




함수 포인터와 void 포인터의

정말 기본적인 내용에 대해 알아보았다.

활용법에 대해서도 충분히 알아야 할 것 같으니

다음 포스팅에서 좀 더 자세히 알아보도록 하겠다.

GoodBye~









STL에 필요한 주요 연산자 오버로딩


이번 시간은 STL에 필요한

연산자 오버로딩에 대해 알아보겠다.

연산자 오버로딩에 대한 기초적인 지식만 있다면

충분히 따라올 수 있는 내용이다.


1. 함수 호출 연산자 오버로딩



2. 배열 인덱스 연산자 오버로딩


3. 멤버 접근 연산자 오버로딩

스마트 포인터란?

일반 포인터를 사용하면 new 연산 후

delete 연산을 꼭 해주어야 한다.(안해주면 메모리 누수)

스마트 포인터는 소멸자에서 자신이

동적으로 생선한 객체를 자동으로

소멸시켜주기 때문에 메모리 누수의 위험이 없다.


4. 타입 변환 연산자 오버로딩





'C++' 카테고리의 다른 글

[C++ STL] STL에 필요한 템플릿 예제  (0) 2016.12.21
[C++ STL] 간단한 콜백 메커니즘  (0) 2016.12.20
[C++] 전방 선언  (0) 2016.12.19
[C++] 팩토리 함수  (0) 2016.12.19
[C++] API란?  (0) 2016.12.15

+ Recent posts