반응형
728x90
반응형

복사 생성자

l  초기화 스타일

다음은 초기화를 나타내는 예제이다.

위와 같이 11번 라인처럼 항상 대입 연산자를 통하여 초기화 해왔으나, 12번 라인과 같이 새로운 C++ 스타일로 초기화가 가능하다. 그리고 두 결과는 값이 대입되었다는 점에서 같다. 이는 복사 생성자와 관련 있는 개념이다.

 

  • 객체의 초기화 방법

객체 초기화 방법 또한 여러 방법이 존재하는데 먼저 38번 라인은 26번 라인에서 정의해 둔 생성자를 통해서 상수를 받아 초기화를 진행한다. 그리고 39번 라인에서는 대입 연산자를 통하여 초기화를 진행한다. C언어에서는 구조체끼리 대입 연산 시킬 경우 멤버 대 멤버의 복사가 일어났다. 마찬가지로 39번 라인 역시 클래스 멤버들에 대해서 멤버 대 멤버 복사가 일어난다. 40번 라인 또한 C++언어 스타일의 초기화 방법으로 결과는 39번 라인과 똑같다. 39번 라인은 결국 40번 라인 형태로 변환이 일어난 후 초기화가 진행된다.

 

그런데 중요한 것은 객체가 생성될 때에는 무조건 생성자가 호출되어야 하는데, 38번 라인의 객체 생성에서는 26번 라인의 생성자가 호출되었다. 그러나 39, 40번 라인의 경우 호출될 수 있는 생성자가 존재하지 않는다. (이미 생성자가 정의되었기 때문에 디폴트 생성자로는 현 상황을 설명할 수 없음)

이러한 경우는 디폴트 복사 생성자가 존재하기 때문에 에러 없이 실행이 가능한 것이다.

디폴트 복사 생성자를 예측해보자면 다음과 같은 형태로 구성되어 있다 복사 생성자를 따로 정의하지 않는다면 멤버 대 멤버의 복사를 진행하는 디폴트 복사 생성자가 자동으로 삽입된다.

다음은 정말 위와 같은 형태로 복사 생성자를 정의한 예제이다.

17번 라인에서 매개 변수를 참조자로 받는 복사 생성자를 직접 정의한 것을 확인할 수 있다. 이러한 복사 생성자는 33번이나 34번 라인과 같이 객체 간의 복사가 이루어질 때 호출되는 것을 확인할 수 있다.  

 

  • explicit

39번 라인의 초기화 형태는 38번 라인 형태의 초기화 형태로 변환이 일어난 후에 복사 생성자를 호출하는데 이러한 변환은 묵시적인 변환이기 때문에 결과를 예측하기 힘들 수 있다. 그렇기 때문에 이러한 묵시적인 변환에 의한 호출을 허용하지 않기 위해서는 다음과 같이 explicit 키워드를 복사 생성자에 붙임으로써 묵시적인 변환을 일으키는 초기화 스타일에 대해 에러 메시지를 내어준다.

  • 얕은 복사와 디폴트 복사 생성자의 문제점

디폴트 복사 생성자는 멤버 대 멤버의 복사를 진행하는데 이러한 방법의 복사 형태를 얕은 복사라고 한다. 일반 변수 형태의 멤버 변수의 경우 문제가 없으나, 멤버 변수로 힙 메모리 영역을 가리키고 있는 포인터를 지니고 있다면 그 때에는 문제가 된다. 다음은 그 문제의 경우를 나타내는 예이다.

분명히 객체를 세 개 생성하였으니 소멸자는 세 번 호출되어야 한다. 그러나 위의 예에서는 소멸자가 딱 한 번 호출되었음을 확인할 수 있다. 소멸자를 호출하면서 delete 연산에서 에러가 나서 프로그램이 종료된 것이다. 그렇다면 delete 연산에서 오류가 난 이유는? 디폴트 복사 생성자의 얕은 복사 때문이다

a1 객체의 생성자가 호출 -> a1 객체 내의 number 할당 -> a2 객체의 디폴트 복사 생성자 호출 -> a1 객체 내의 number 가 가지고 있는 주소 값을 a2 객체 내의 number에 대입 -> a3 객체의 디폴트 복사 생성자 호출 -> a2 객체 내의 number 가 가지고 있는 주소 값을 a3 객체 내의 number에 대입

여기 까지는 생성자 호출 과정이다. 여기서 문제는 세 개의 객체들의 생성자가 모두 호출되면 a1, a2, a3 객체 내의 number는 모두 같은 메모리 위치를 가리키고 있다는 것이다. 그렇기 때문에 첫 소멸자에 해체한 이미 메모리를, 두 번째 소멸자의 실행에서 한번 더 해체하려는 과정에서 오류가 나는 것이다.

이렇게 멤버로 동적 할당이 필요한 객체 간의 복사 생성자는 멤버 대 멤버의 복사의 얕은 복사 형태로 이루어져서는 절대 안 된다. -> 깊은 복사를 위한 복사 생성자를 꼭 정의해야 한다.

다음은 깊은 복사를 행하는 복사 생성자를 정의한 예이다.

18번 라인에서 깊은 복사를 수행하는 복사 생성자를 정의한 결과 소멸자가 모두 정상 출력되는 것을 확인할 수 있다.

 

  • 복사 생성자가 호출되는 시점

복사 생성자가 호출되려면 먼저 공통적으로 꼭 포함해야 할 기본 전제에 대해서 알고 있어야 한다.

1.     객체를 새로 생성해야 함

2.     그리고 생성과 동시에 동일한 자료형의 객체로 초기화 해야 함.(할당과 동시에 초기화)

 

복사 생성자의 호출되는 시점은 다음과 같이 세 가지 경우에 호출되는 시점이 될 수 있다. 이 세 가지 경우 모두 겉보기에는 다른 형태이지만 결국에는 객체를 새로 생성하고, 생성과 동시에 동일 자료형의 객체로 초기화 되는 경우인 것이다.

1.     기존에 생성된 객체를 이용해서 새로운 객체를 초기화 하는 경우

-> int num1=num2; 와 같은 상황으로 할당과 동시에 초기화가 일어 남.
-> AAA a1=a2; 의 경우 a1의 복사 생성자가 호출되어 a2의 값이 a1으로 복사됨.

2.     Call-by-Value 방식의 함수 호출 과정에서 객체를 인자로 전달하는 경우

-> 함수가 호출되는 순간에 매개 변수가 할당됨과 동시에 전달된 인자의 값으로 초기화된다.
-> void func(AAA arg);에서 func(a1);으로 호출할 경우 arg의 복사 생성자가 호출되어 a1의 값이 arg로 복사됨.

3.     객체를 반환하되, 참조형으로 반환하지 않는 경우

-> 함수가 값을 반환하면, 별도의 메모리 공간이 할당되고, 이 공간에 반환 값이 저장(초기화)된다. 이런 식으로 변수 혹은 객체로서 별도 메모리에 저장되어 임시 변수 혹은 객체로 남아 있어야 함수 등을 이용하여 출력을 하거나, 변수에 대입을 할 수 있기 때문이다. 이렇게 객체가 반환될 때, 임시 객체의 복사 생성자가 호출된다.

이렇게 생성된 임시 객체는 다음 행으로 넘어가면 바로 소멸되어 진다. , 참조자에 의해 참조될 경우 그 임시 객체는 바로 소멸되지 않는다.

 

  • 참고

클래스 외부에서 객체의 멤버 함수를 호출하기 위해 필요한 것.

객체에 붙여진 이름, 객체의 참조 값(객체 참조에 사용되는 정보), 객체의 주소 값

 

 

 

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

17 상속  (0) 2020.09.14
16 friend, static, const, mutable  (0) 2020.09.14
14 클래스와 배열 및 this 포인터  (0) 2020.09.13
13 생성자와 소멸자  (0) 2020.09.12
11, 12 정보 은닉과 캡슐화  (0) 2020.09.11
728x90
반응형

클래스와 배열 및 this 포인터

  • 객체 배열

객체 배열은 다음과 같이 일반 배열 방식으로 선언할 수도 있으며, 동적 할당을 통한 방식으로 선언할 수 있다.

 

다만, 동적 할당이든 일반 배열이든 객체 배열을 선언할 때에는 각 배열의 요소 객체들마다 생성자 및 소멸자를 호출하지만 생성자의 경우 호출할 생성자를 명시하지 못한다 인자 전달이 불가능하고 무조건 인자가 없는 생성자만을 호출한다. , 인자가 없는 생성자가 반드시 정의되어 있어야 한다. 각 배열의 요소를 초기화 시키기 위해서는 일일이 초기화를 시켜줘야 한다.

  • 객체 포인터 배열

객체 포인터 배열은 객체의 주소 값 저장이 가능한 포인터 변수로 이뤄진 배열이다.

  • this 포인터

this 포인터는 객체 자신을 가리키는 용도로 사용되는 포인터로 멤버 함수 내에서 this라는 이름으로 사용할 수 있는 포인터이다. 객체 자신의 주소 값을 나타내는 포인터이다.

11번 라인에서 출력한 this 포인터를 출력한 결과와 17번 라인의 객체 a의 주소 값을 출력한 결과가 같은 것으로 보아 this 포인터는 객체 자기자신의 주소를 나타내는 포인터이며 다음과 같이 활용한다.

  • 자기 참조자의 반환

This 포인터를 이용하여 자기 참조가 가능하도록 참조 정보를 반환할 수 있다. 17번 라인에서 참조 정보가 반환되고, 30번 라인에서 거듭된 접근 연산을 통해 멤버 함수를 호출하는데 이는 참조 값을 반환하고 그 반환된 참조 값을 이용해서 다시 접근하기를 반복하기 때문에 가능하다.

28번 라인에서 대입 연산자를 기준으로 a의 참조 정보(참조 값이) rA 참조자에 전달이 된다.

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

16 friend, static, const, mutable  (0) 2020.09.14
15 복사 생성자  (0) 2020.09.13
13 생성자와 소멸자  (0) 2020.09.12
11, 12 정보 은닉과 캡슐화  (0) 2020.09.11
10 클래스와 객체  (0) 2020.09.11
728x90
반응형

생성자와 소멸자

  • 생성자

위의 예제에서 11번 라인에 나타나는 함수를 생성자라고 한다.

생성자는 먼저 클래스의 이름과 동일한 함수의 형태를 띄고 있으며, 반환형이 선언되어 있지 않으며, 실제로 반환 또한 하지 않는다.

생성자의 특징으로는 객체 생성 시 딱 한번 호출되며, 함수의 일종이니 오버로딩이 가능하며, 디폴트 매개변수를 설정할 수 있다. 그리고 객체가 생성될 때에는 생성자가 무조건 호출된다. 생성자가 호출되지 않고서 객체가 생성될 수 없다.

위의 예제는 생성자를 선언하고 활용하는 예이다. 11번 라인부터 23번 라인에서는 생성자를 정의하고 있는데 생성자도 일종의 함수이기 때문에 함수 오버로딩이 적용되고, 디폴트 매개변수의 적용 또한 가능함을 볼 수 있다. 그리고 29번 라인 ~ 42번 라인까지 일반 변수 형태의 선언과 동적 할당 형태의 선언으로 나뉘어서 각각 선언될 시 생성자의 호출을 볼 수 있다. 32번 라인의 형태는 객체가 생성되지 않는다. 왜냐하면 반환 타입이 AAA형이고 매개 변수는 void형이고 함수 이름이 a0인 함수의 원형 선언으로 간주되기 때문이다.

 

  • 멤버 이니셜라이저

다음 상황에서 멤버 이니셜라이저는 멤버 변수로 선언된 객체의 생성자 호출에 활용된다.

28번 라인을 통해서 멤버 이니셜라이저를 진행하고 있는데 AAA 클래스는 BBB의 클래스의 객체를 멤버 변수로 가지고 있다. 객체 b의 초기화는 첫 번째 방법으로는 객체 b를 이용하여 set 함수를 통해 혹은 그에 준하는 함수의 호출을 통해 정보 은닉에 위배되지 않게 b 객체의 number 멤버 변수를 초기화 시키거나, 두 번째 방법으로는 생성자를 이용하여 초기화를 시켜야 한다. 그러나 위와 같이 클래스 내에서 선언되어 객체의 생성과 동시에 초기화 값 전달이 불가능하다. (BBB b(123) 형태의 선언이 불가능하다는 의미) 그러나 멤버 이니셜라이저를 이용하면 객체 형태의 멤버의 생성자를 이용한 초기화가 가능하다. 28번 라인의 내용이 멤버 이니셜라이저이다. 위 예제에서의 의미는 객체 b의 생성 과정에서 num을 인자로 전달받는 생성자를 호출한다라는 의미이다.

위와 같이 객체가 아닌 일반 기본 자료형에 대한 멤버 변수 또한 이니셜라이저를 통해서 초기화가 가능하다. 12번 라인처럼 멤버 변수 num을 생성자를 통해 넘겨온 매개 변수 n을 이용하여 초기화 하라는 의미이다. 이렇게 되면 멤버 변수를 초기화 하는 방법은 두 가지가 된다.

1.     생성자의 몸체에서 초기화
2.     이니셜라이저를 이용한 초기화

두 가지가 존재하는데 이니셜라이저를 통하여 초기화를 진행하는 경우 다음과 같은 이점을 얻을 수 있다.

1.     초기화의 대상을 명확히 인식할 수 있다.
2.     성능에 약간의 이점이 있다.
->  선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성된다.
->  생성자 몸체의 경우 int num; num=10;/이니셜라이저의 경우 int num=10; 효과를 볼 수 있다.

const 멤버 변수/참조자 멤버 변수의 초기화

이니셜라이저를 이용할 경우 선언과 동시에 초기화와 같은 효과를 얻는다는 이유로 const 멤버 변수의 초기화를 진행할 수 있다! 또한 선언과 동시에 초기화가 이루어져야 하는 참조자 멤버 변수 또한 초기화가 가능하다.

  • 객체 생성 과정의 정리

1.     메모리 공간의 할당
2.     이니셜라이저를 이용한 멤버 변수(객체)의 초기화
3.     생성자의 몸체 부분 실행

모든 객체는 세 과정이 순서대로 거치고 생성이 완료된다.

  • 디폴트 생성자

객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다. 그러나 생성자가 없는 클래스의 경우 객체를 생성하지 못하는 것인가? 이런 문제를 해결하기 위해서 생성자를 정의하지 않은 클래스에는 C++ 컴파일러에 의해서 디폴트 생성자가 자동으로 삽입된다.

디폴트 생성자는 전달 인자를 받지 않고, 내부적으로 아무런 일도 하지 않는 생성자이다.

이러한 디폴트 생성자는 생성자가 하나도 정의되어 있지 않을 때만 컴파일러에 의해 자동으로 삽입된다. 다른 말로 생성자가 하나라도 추가되어 있다면 디폴트 생성자는 컴파일러에 의해 자동으로 삽입되지 않는다.

  • Private 생성자

클래스 내부에서만 객체의 생성을 허용하려는 목적으로 생성자를 private으로 선언.

  • 소멸자

위와 같은 예에서 12번 라인과 같은 형태를 소멸자라고 한다.

소멸자는 클래스의 이름 앞에 ‘~’가 붙은 형태의 이름을 갖으며, 반환형이 선언되어 있지 않고 실제로 반환하지 않는다. 그리고 매개변수는 항상 void형으로 선언되어야 하고 오버로딩 및 디폴트 매개변수 설정 모두 불가능하다.

소멸자의 특징은 먼저 객체 소멸 과정에서 자동으로 호출이 된다. 그리고 아무런 소멸자가 생성되어 있지 않으면 디폴트 생성자가 컴파일러에 의해 자동으로 삽입된다. 소멸자는 생성자에서 할당한 리소스의 소멸에 사용된다.

다음은 소멸자 사용의 예이다.

 

 

 

 

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

15 복사 생성자  (0) 2020.09.13
14 클래스와 배열 및 this 포인터  (0) 2020.09.13
11, 12 정보 은닉과 캡슐화  (0) 2020.09.11
10 클래스와 객체  (0) 2020.09.11
09 C언어의 표준 함수 호출  (0) 2020.09.10
728x90
반응형

정보 은닉

  • 정보 은닉

정보 은닉이란? 멤버 변수를 private 접근 제어 지시어로 선언하여 외부에서의 모든 접근을 차단하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버 변수의 접근 및 초기화, 갱신 등이 이루어지도록 유도하는 것이 정보 은닉이다.

이러한 정보 은닉이 나오게 된 이유는 멤버 변수의 외부 접근 허용으로 대입되는 값이 전혀 검증이 이루어지지않은 채로 대입되기 때문이다. 그렇기 때문에 외부의 접근을 허용하지 않고(private), 해당 멤버 변수에 접근할 수 있는 함수를 별도로 정의하고 그 함수 내에 해당 멤버 변수에 대입하려는 값에 대한 검사를 이룰 수 있도록 하는 것이다.

8번의 private 접근 제어 지시자를 통하여 정보 은닉의 조건 중 하나를 충족시켰고, 13, 23번 라인에서 멤버 변수에 접근에 대해서 검증을 함으로써 정보 은닉을 성립시킨다. 13, 23, 33, 34번 라인에서 get, set 함수를 볼 수 있는데 이러한 함수들을 엑세스 함수라고 한다. 이러한 엑세스 함수들은 당장 사용되지는 않지만 필요할 수 있는 가능성이 있기 때문에 항상 생성해두기도 한다.

  • const 함수

위의 예제에서 get 메소드에 대해서 변경된 부분이 있다.

Get 엑세스 함수에 const 키워드가 붙은 형태인데 이러한 함수를 const 함수라고 한다. get 엑세스 함수만 적용되는 것이 아닌 모든 함수들이 const 키워드만 붙어 있다면 적용된다.

이러한 함수에 const가 붙게 되면 의미하게 되는 것은 이 함수 내에서는 멤버 변수에 저장된 값을 변경하지 않겠다 라는 의미를 갖게 된다. , const 함수에서는 멤버 변수의 값을 변경하는 코드가 존재하면, 혹은 그럴 가능성, 변경할 수 있는 능력이 존재하는 코드가 존재한다면 컴파일 에러를 발생한다.

멤버 변수의 값을 변경할 수 있는 능력이나 가능성이 존재하는 코드란 const화 되지 않은 함수의 호출을 뜻한다. 왜냐하면 const화 되지 않은 함수는 호출됨으로써 멤버 변수를 어떠한 형태로든 변경할 수 있는 능력과 가능성이 존재하기 때문이다. 그렇기 때문에 const 함수 내에서는 const화 되지 않은 함수의 호출을 제한한다.

+ const 참조자를 이용한 함수 호출 또한 const화된 함수만을 호출할 수 있다.

위와 같은 두 가지 조건에 의해서 const 함수를 선언하고 사용하게 되면 많은 함수들을 const화 시켜야 한다. 그렇지만 그만큼 작성된 코드는 안정성이 높아진다.

캡슐화

  • 캡슐화

캡슐화는 관련 있는 함수와 변수를 하나의 클래스 안에 묶는 것. 그러나 어려운 개념이다 왜냐하면 캡슐화의 범위를 결정하는 일이 쉽지 않기 때문이다.

캡슐화에는 정보 은닉 개념이 기본적으로 포함 된다.

728x90
반응형
728x90
반응형

클래스와 객체

  • 구조체

연관 있는 데이터를 하나로 묶으면 프로그램의 구현 및 관리가 용이하다.

소프트웨어란 데이터의 표현 + 데이터의 처리로 표현이 되는데, 여기서 데이터는 항상 관련 있는 주제, 특징, 특성에 따라 부류를 형성한다. 이러한 부류를 형성하는 데이터들은 항상 함께 생성되고, 함께 사용되고, 함께 소멸되는 특성이 있고, 그래야만 하는 경우가 대부분이다. 구조체는 연관 있는 데이터를 묶고 부류를 형성할 수 있도록 하는 문법적 장치이다.

위와 같이 구조체를 생성하고, 사용할 수 있다. 13번 라인에서는 C언어와 다르게 struct 키워드를 사용하지 않고 구조체를 선언할 수 있도록 문법적으로 구성되어 있다.

  • 구조체에 함수 삽입하기

구조체는 연관 있는 부류의 데이터를 묵는 문법적인 수단이었다. 그렇다면 소프트웨어의 구성에서 데이터의 처리 부분에 해당하는 함수 또한 관련 데이터 부류와 함께 묶어서 처리하면 되지 않을까?

위의 예제에서 데이터 표현 부분은 관련 부류를 형성하여 구조체를 이루었다. 그러나 deposit, withdraw 함수는 기능적으로 account 구조체의 데이터 부류에 대해서 처리를 담당하는 함수 임에도 불구하고 구조체와 서로 분리되어 있다. 이 함수들 또한 부류를 형성하여 같이 묶는다면 다음과 같다.

                          <a. 멤버 함수를 클래스 내부에 정의>                                                <b. 멤버 함수를 클래스 외부에 정의>             

관련 있는 부류의 데이터와 처리에 해당하는 모든 부분을 하나의 구조체에 넣은 형태이다. 이런 식으로 구조체안으로 들어간 함수를 해당 구조체의 멤버 함수라고 한다. 구조체로 들어간 함수를 호출할 때에는 a 그림의 32번 라인, 33번 라인과 같이 구조체의 멤버 변수를 지정할 때와 마찬가지로 직접 접근 연산자 (혹은 포인터의 경우 간접 접근 연산자)를 이용하여 함수를 호출한다.

그림 a와 그림 b의 차이는 함수의 정의를 구조체 내부에 하는지, 구조체 외부로 따로 정의하는 지의 차이이다. 함수의 정의가 너무 길고 복잡할 때에는 가독성을 위해 함수의 선언 만을 구조체 정의 내부에 두고 함수의 정의는 구조체 외부에 따로 둔다. 이 때 구조체 이름과 범위 지정 연산자를 이용하여 함수의 정의를 외부에 두는데 그림 b25, 31, 36번 라인과 같은 형식으로 정의하면 된다. 구조체 내부에 함수의 정의가 존재하면 함수를 인라인(in-line)으로 처리하라는 의미이다. 함수를 구조체 외부에 둔 경우에 인라인 처리를 원한다면 따로 출력 타입 앞에 inline 키워드를 넣음으로써 인라인 화 할 수 있다.

지금까지의 구조체는 모두 클래스의 일종으로 간주되며, 멤버 변수와 멤버 함수를 넣을 수 있었던 것 모두 클래스의 일종이기 때문이고 이러한 정의의 구조체를 ‘클래스’ 라고 표현할 수 있다.

  • 클래스와 구조체의 차이점

위의 왼쪽 예는 구조체라고 정의되어 있지만 사실 멤버 함수를 포함할 수 있는 클래스의 일종 혹은 클래스라고 할 수 있다. 그렇기 때문에 오른쪽과 같이 키워드를 class로 변경하고 클래스를 정의한다.

그러나 왼쪽 16번 라인에서 구조체일 때는 가능했던 초기화가 클래스로서 인정하고 변경을 하고 보니 오른쪽 16번 라인에서는 초기화가 불가능하다. 16번 라인과 같은 초기화 뿐만 아니라 다음과 같은 경우도 불가능하다.

위의 예의 공통점들은 클래스의 외부에서 클래스 내의 모든 멤버들에 접근하여 쓰는 혹은 읽는 작업을 하고 있다. 즉 외부에서 접근한다는 것이 공통점이다. 외부에서 접근하는 모든 문장에 대해서는 컴파일 에러를 내고 있다.

클래스는 접근에 대한 지시를 따로 명시하지 않는다면 기본적으로 외부로부터의 모든 접근을 허용하지 않으며, 이 때 클래스 내에서 선언된 모든 멤버들은 모두 클래스 내에서만 접근하는 내부 접근만 가능하다. 이렇게 클래스는 구조체와 다르게 외부 접근에 대해서 허용 범위를 별도로 선언해야 하는 것이 차이점이다.

  • 접근 제어 지시자(접근 제어 레이블)

위의 예제는 접근 제어 지시자를 적용한 account 클래스를 나타낸 것이다. 먼저 접근 제어 지시자의 효력의 범위는 접근 제어 지시자를 명시한 라인부터 다음 접근 제어 지시자가 나오는 라인 바로 전 까지가 효력이 끼치는 범위이다. , 8번 라인의 private 영역의 효력은 다음 접근 제어 지시자인 public의 바로 전 라인인 11번 라인까지 private의 효력을 유지한다. 8번 라인부터 11번 라인까지 있는 멤버 변수들은 외부에서 접근이 절대적으로 금지된다. 반면에 12번 라인부터 클래스의 끝 까지는 public 접근 제어 지시자의 영역으로 멤버 함수들에 대해서는 외부에서의 접근을 허용한다. 그러므로 외부에서는 account 클래스의 멤버 함수에 접근하여 원하는 기능을 실행할 수 있도록 적절한 멤버 함수를 정의 및 선언해주어야 하고, 이러한 멤버 함수를 통해 멤버 변수의 조작이 이루어져야 한다. 아니 그렇게 설계되어야 한다!

  • 파일 분할 시 클래스의 선언과 정의에 대한 정보

클래스의 선언

컴파일러가 클래스와 관련된 문장의 오류를 잡아내는데 필요한 최소한의 정보, 클래스를 구성하는 외형적인 틀을 보여준다. 위의 예에서 account 클래스와 관련된 문장의 옳고 그름을 판단하는데 사용된다.

-> 컴파일의 정보로 사용되므로 헤더 파일에 저장한 후, 필요한 위치에 포함될 수 있도록 하면 됨.
, 인라인 함수의 경우에는 함수 정의가 분리되었다고 하더라도 헤더 파일에 함께 포함해야 한다. 왜냐하면? 컴파일 과정에서 함수의 호출 문이 있는 곳에 함수의 몸체가 대치되어야 하기 때문이다.

클래스의 정의

멤버 함수의 정의는 다른 문장의 컴파일에 필요한 정보를 가지고 있지 않다. 따라서 컴파일 된 이후에 링커에 의해 하나의 실행파일로 묶이기만 하면 된다.

-> 소스 파일에 저장해서 컴파일이 되도록 하면 된다.

  • 객체지향 프로그래밍

먼저, 객체는 현실에 존재하는 모든 사물 또는 대상이 될 수 있다. 이런 객체의 개념을 이용한 객체지향 프로그래밍이란? 현실에 존재하는 사물과 대상, 그리고 그에 따른 행동을 있는 그대로 실체화를 시키는 형태의 프로그래밍

이러한 객체를 이루는 것은 하나 이상의 상태 정보(데이터)하나 이상의 행동(기능)으로 구성된다

클래스 기반의 객체 생성 방법 및 활용 (두 가지)

   - 일반적인 변수 선언 방식

   - 동적 할당 방식

Message Passing 방법

 -> 하나의 객체가 다른 하나의 객체에게 메시지를 전달하는 방법은 함수 호출을 기반으로 한다! 함수 호출을 기반으로 하는 객체 간의 대화법에서 함수 호출하는 행위를 메시지 전달이라고 함.

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

13 생성자와 소멸자  (0) 2020.09.12
11, 12 정보 은닉과 캡슐화  (0) 2020.09.11
09 C언어의 표준 함수 호출  (0) 2020.09.10
08 Malloc과 free의 대체 – new와 delete  (0) 2020.09.10
07 참조자(Reference)  (0) 2020.09.08
728x90
반응형

c언어의 표준 함수 호출

  • C언어의 라이브러리를 사용하기 위한 헤더 삽입

위와 같이 헤더를 선언하면 C함수의 라이브러리를 C++에서도 사용할 수 있다.

그러나 C언어 스타일로 헤더를 선언하고, 함수를 호출한다고 하더라도 그 자체를 허용한다. 그 이유는 하위 버전과의 호환성(backwards compatibility)를 제공한다.

위와 같은 헤더 삽입과 함수 호출은 허용하는 것이 바로 하위 버전과의 호환성을 제공한다고 한다.

그러나 오버로딩 되는 라이브러리 등의 문제로 모두 호환되는 것은 아니다.

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

11, 12 정보 은닉과 캡슐화  (0) 2020.09.11
10 클래스와 객체  (0) 2020.09.11
08 Malloc과 free의 대체 – new와 delete  (0) 2020.09.10
07 참조자(Reference)  (0) 2020.09.08
06 이름 공간(namespace)  (0) 2020.09.08
728x90
반응형

Mallocfree의 대체 – newdelete

  • C언어에서의 동적 할당

C언어에서 동적 할당의 단점은 할당 크기를 바이트 단위로 직접 명시해주어야 하고, 반환되는 void *에 대해서 적절한 타입 변환이 이루어지고 대입이 이루어져야 한다.

C++에서의 newdelete 연산자를 사용하면 위의 단점을 모두 커버할 수 있다.



  • C++에서의 동적 할당

다음과 같은 형식으로 동적 할당을 행한다.

그리고 다음은 C 스타일의 동적 할당과 C++ 스타일의 동적 할당을 비교한 것인데 완전히 같은 내용이다.

  • C++의 클래스의 객체 생성 시 new & delete

객체 생성을 할 때에는 꼭 new 연산자를 이용하여 동적 할당하고, delete 연산자를 이용하여 할당을 해체해야 한다. -> Cmalloc & free 함수로 동적 할당 및 해체를 진행할 경우 생성자 / 소멸자의 호출이 이루어지지 않음.

  • 참조자를 통한 힙 영역 참조

참조자를 이용하여 포인터 연산 없이 접근해볼 수 있다.



728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

10 클래스와 객체  (0) 2020.09.11
09 C언어의 표준 함수 호출  (0) 2020.09.10
07 참조자(Reference)  (0) 2020.09.08
06 이름 공간(namespace)  (0) 2020.09.08
05 인라인 함수(inline function)  (0) 2020.09.07
728x90
반응형

참조자(Reference)

  • 참조자

참조자란 할당된 메모리 공간에 둘 이상의 이름을 부여하는 것으로 다시 말하면 참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름

다음은 참조자를 사용하는 예제이다.

10번 라인에서 & 연산자를 이용하여 참조자(레퍼런스)를 선언하고, 변수 num에 대해서 참조를 진행한다. 참조자 선언과 참조를 마친 후 13번 라인에서 일반 변수 num과 레퍼런스 ref의 주소를 출력해보았을 때 완전히 같은 주소를 출력하는 것을 확인할 수 있는 것으로 미루어 보아, 기존의 대상이 되는 변수에 이름만 부여되는 것임을 확인할 수 있다. , 참조자는 이름을 하나 더 부여하는 별칭인 셈이다.

그리고 하나의 변수에 대해 여러 참조자를 선언하고 참조할 수 있으며, 참조자를 대상으로 참조자를 선언할 수 있다.

9번 라인부터 13번 라인까지 대상 변수에 대한 참조자의 수, 참조자를 대상으로 하는 거듭된 참조까지 가능함을 알 수 있다. 그리고 19번 라인에서 대상 변수 그리고 참조자들의 주소를 출력했을 때 모두 대상 변수의 주소로 같게 나왔음을 확인할 수 있다.

참조자의 잘못된 선언 형태에 대한 간단한 예제이다.

먼저, 10번 라인에서는 참조자를 선언만 하고, 참조를 하지 않은 상태인데 참조자는 선언과 동시에 어떤 대상을 꼭 참조해야만 한다. 그리고 11번 라인에서는 선언과 동시에 참조를 진행했지만 참조의 대상은 참조자의 타입이 맞는 변수가 대상이어야 한다. 마지막 12번 라인에서는 NULL로 선언과 동시에 참조를 하고 있지만 역시 마찬가지로 변수가 대상이 아니기 때문에 참조가 불가능하다. 이와 더불어 한 번 참조가 된 이후에는 참조의 대상 변경이 불가능하다.

참조의 가능 범위는 다음과 같이 크게 3가지가 존재한다.

일반 변수

배열 요소

포인터 변수

  • 참조자와 함수

Call-By-AddressCall-By-Reference 모두 주소 값을 인자로 전달하는 형태의 함수 호출은 뜻하는데, 그 사실 보다는 주소 값을 전달하되, 해당 주소를 이용하여 참조 혹은 값의 변경이 일어났는가를 볼 수 있어야 함. (결국, call-by-addresscall-by-reference 방법은 서로 비슷한 방법이라고 보일 수 있지만 전자는 단지 주소만을 전달하느냐 후자는 주소를 전달하고 참조를 행하느냐를 보는 것인데 전자의 경우는 주소만 전달될 뿐 참조가 일어나지 않기 때문에 call-by-value로 볼 수도 있음 그렇기 때문에 두 용어는 구분이 필요함을 이야기함.)

다음은 참조자를 이용한 swap 함수의 구현이다.

  • Const 참조자

참조자를 이용하여 call-by-reference를 구현할 때, 매개 변수에 참조자를 const화 시켜서 넣게 되면 해당 참조자 매개 변수를 이용하여 값의 변경을 허용하지 않겠다 라는 의미를 갖는다.

이러한 의미가 왜 필요한가?



위와 같은 예제를 마주쳤을 때, C언어에서는 num의 값이 바뀌지 않는 call-by-value 방식의 호출이라는 것을 위 예제만 보고도 알 수 있으나, C++언어에서는 참조자의 개념 때문에 call-by-reference인지 call-by-value인지 알 수가 없다. 이를 확인하기 위해서는 함수의 원형을 확인해야 하고 뿐만 아니라 정의까지 확인해야한다. (왜냐하면 참조자로 매개 변수를 선언했을지라도 참조 후 값의 변경이 있는지 없는지는 정의를 확인해야하기 때문이다.) 이러한 불편함을 해소하기 위해서 함수의 원형 선언만 보더라도 const 키워드로 인해 값의 변경이 일어나는지 일어나지 않는지를 확인할 수 있게 해준다. 다음과 같이.

그리고 const 참조자에는 이러한 경우도 있는데,

16번 라인에서 const를 이용한 변수의 상수화로 심볼릭 상수를 선언하였고, 17번 라인에서 ref 참조자로 심볼릭 상수와 참조 관계를 형성한다. 그리고 18번 라인에서 참조자를 통한 값의 변경을 행한다. 기껏 16번 라인에서 상수로 만들었는데 참조자를 통한 값을 변경을 하고 있다. 그러나 이는 허용하지 않는다. 17번 라인 참조 관계를 형성하는 부분에서부터 허용을 하지 않는다 왜냐하면?

C언어 정리에서 언급했듯이 대입 연산자를 기준으로 l-value r-value의 자료형은 완벽하게 같아야 대입이 이루어지는데 현재 17번 라인에서는 l-value의 타입은 int& 이고, r-value의 타입은 const int 이다. 이는 다른 타입이기 때문에 대입에서 오류가 나는 것임.

그래서 위와 같이 맞춰 주어야만 오류가 나지 않는다.

const 참조자는 상수를 참조할 수 있는 특징을 가지고 있다. 일반 참조자와 다르게 const 참조자를 이용해서상수와 참조 관계를 형성하려고 하면 상수가 해당 라인이 넘어가면 소멸되는 리터럴 상수가 아닌 임시 변수 형태로 생성되어 그 값을 가지고 있는 형태로 생성된다. 그렇기 때문에 그 임시 변수 공간과 참조 관계를 형성할 수 있는 것이다.

15번 라인에서 50이라는 임시 변수를 생성하고 그 임시 변수의 공간에 ref 이름을 부여한다. 7번 라인과 16번 라인 또한 같은 맥락이다.

  • 함수의 출력 타입이 참조형인 경우

 

위와 같은 함수 정의에서 14번 라인의 경우에는 arg 변수에 대한 새로운 참조 관계가 형성되는 경우이고, 15번 라인에서는 200이라고 하는 값이 반환된 것이고 v에는 값이 저장되는 경우이다.

위와 같은 경우에는 반환 값을 참조가 아닌 일반 자료형으로 지정한 경우인데 이 경우 반환되는 값은 단순히 값만 반환되기 때문에 일반 변수로 값을 리턴 값을 받을 수 있지만, 25번 라인처럼 참조 관계를 형성할 수는 없다 왜냐하면 int& r = 200; 형태와 같은 형태이기 때문이다.

위와 같이 함수 내의 지역 특성을 지니는 지역 변수에 대해서 참조 관계를 형성시켜서는 안된다(왼쪽). 이는 포인터로 지역 변수에 대해서 가리키고 참조하는 것과 다를 것이 없다.(오른쪽).
함수가 종료되면 사라지는 지역적 특성을 지니는 지역 변수를 함수의 외부(함수 종료 상태의 지역)에서 참조한다는 것은 이미 해체되어 없는 공간 혹은 다른 쓰레드 작업에 의해 이미 다른 중요 값으로 채워져 있는 공간을 참조하고 조작할 수 있는 가능성이 있기 때문에 잘못된 것.





728x90
반응형
728x90
반응형

이름 공간

  • 이름 공간이 존재 등장한 이유?

변수, 함수 등의 이름 충돌을 막기 위함이다.

  • 이름 공간

위와 같은 예제에서는 두 함수 모두 수행하는 내용은 다르지만 이름, 매개 변수가 같다는 이유로 오버로딩이 적용되지 않고 에러가 난다. 예제에서는 나타나지 않지만 두 함수 모두 다른 모듈이나 소스에 이미 의존되어 있는 부분이 있기 때문에 이름을 바꾸거나 할 수 없다. 이러한 경우 이름 공간을 이용한다.

위의 예제에서는 각 함수를 A, B라는 이름 공간(namespace) 으로 감싸고, 영역을 구분하였다. 영역을 구분하였기 때문에 함수 중복 문제로 오류가 발생하지 않는다. 그리고 24, 25번 라인에서는 ‘ :: ’ 범위 지정 연산자를 이용하여 이름 공간을 지정하여 그 내부의 함수를 지정하여 호출하는 식으로 함수 호출을 진행함.

  • 이름 공간 기반의 함수 선언과 정의 구분

이름 공간에서 동일한 이름 공간 내의 함수 호출 및 변수 참조가 일어날 경우 범위 지정 연산자를 이용하여 이름 공간을 명시할 필요가 없다.

  • 이름 공간의 중첩

이름 공간은 중첩이 가능하고, 범위 지정 연산자를 거듭 이용하여 범위를 지정하고 변수나 함수에 접근한다.

  • 이름 공간 std

std::cout, std::cin, std::endl 등은 모두 이름 공간 내의 어떤 요소들이었다.

위와 같은 형태로 존재하고 있는 요소들이고, cout/cin/endl 은 추후에 정리.

  • using 연산자를 이용한 이름 공간의 명시

위와 같이 이름 공간 전체를 명시하여 이름 공간의 범위 지정 연산을 생략할 수 있다.

위와 같이 이름 공간 내의 특정 요소를 지정하여 이름 공간 범위 지정을 생략할 수 있다.

아래는 이름 공간을 명시 혹은 이름 공간 내의 특정 요소에 이름 공간을 명시하여 범위 지정 연산을 생략한 예를 보여준다.

 

728x90
반응형
728x90
반응형

인라인 함수

  • C언어 기반의 매크로 함수

매크로 함수의 장점은 일반적인 함수에 비해서 실행 속도의 이점이 있다. 또한 매크로 함수의 단점으로는 정의하기가 어렵고 복잡한 함수를 매크로 형태로 정의하는데 한계가 있다.

매크로 함수의 몸체 부분이 매크로 함수의 호출 부분을 완전히 대체하여 SQUARE(2) -> ((2)*(2)) 형태로 대체되어 컴파일 된다. 이렇게 함수의 몸체 부분이 함수의 호출 문장을 완전히 대체했을 때 함수가 인라인화 되었다 라고 표현한다.

그렇지만 매크로 함수는 정의하기가 복잡하니 일반 함수처럼 정의 가능하도록 함. 그것이 C++의 인라인 함수이다.

  • C++ 언어 기반의 인라인 함수

함수를 정의한 후 inline 키워드만 붙여 넣어주면 인라인 함수가 정의 된다.

  • C언어 기반의 매크로 함수와 C++언어 기반의 인라인 함수의 비교

728x90
반응형

'컴퓨터 언어 정리 > C++ 언어' 카테고리의 다른 글

07 참조자(Reference)  (0) 2020.09.08
06 이름 공간(namespace)  (0) 2020.09.08
04 디폴트 매개변수(Default Parameter)  (0) 2020.09.07
03 함수 오버로딩(Function Overloading)  (0) 2020.09.06
02 Bool 자료형  (0) 2020.09.06

+ Recent posts