[C++] 2. C언어 기반의 C++(2)


새로운 자료형 bool



C와 C++ 모두 정수 0은 ‘거짓’, 0이 아닌 정수는 ‘참’을 의미합니다~!

게다가 C++에서는 참과 거짓의 표현을 위한 키워드 true와 false를 정의하고 있는데,

true는 정수로 출력하면 1, false는 정수로 출력하면 0이지만,

둘 다 정수로 출력가능한 것이지 자료형이 정수는 아닙니다!

이 둘은 참과 거짓을 표현하기 위한 1바이트 크기의 데이터일 뿐입니다.

그래서 이들을 저장하기 위한 자료형이 별도로 정의되는 것은 당연한 일이지요~!

그 자료형이 바로 bool 입니다!

bool isTrue=true; //true 저장

bool isFalse=false; //false 저장



참조자의 이해



할당된 하나의 메모리 공간에 둘 이상의 이름을 붙이기 위해

C++에서는 참조자라는 변수를 제공합니다!

int num1=2010;

int &num2=num1; //참조자 선언

num2=100; //num1도 같이 바뀝니다!



위에서 보듯이 num2는 num1의 참조자가 되며,

num1이라 이름 붙어있는 메모리 공간에 num2라는 이름이 하나 더 붙습니다!

참조자는 자신이 참조하는 변수를 대신할 수 있는 또 하나의 이름입니다.

여기서 &는 주소 값을 반환하는 연산자가 아니라 참조자의 선언을 뜻하게 됩니다.

참조자의 수는 제한이 없으며, 참조자를 대상으로도 참조자를 선언할 수 있습니다.

따라서 다음과 같은 선언을 할 수 있습니다.


int num1=1;
int &num2=num1;
int &num3=num1;

//num1, num2, num3 모두 같은 메모리 공간을 가리킵니다.

int num1=1
int &num2=num1;
int &num3=num2;



그러나 참조자는 변수에 대해서만 선언이 가능하고, 선언됨과 동시에 누군가를 참조해야합니다~

따라서 다음과 같이 상수를 참조하거나 참조자 선언만 할 수 없습니다.


int &ref=30; (X)

int &ref; (X)

int &ref=NULL; (X) //NULL로 초기화도 안 됨



포인터 변수도 변수기에 참조자의 선언이 가능합니다.


int num=12;

int *ptr=#

int *(&pref)=ptr; //포인터변수 참조

//다음 선언은 같습니다

std::cout<<num<<std::endl;

std::cout<<*ptr<<std::endl;

std::cout<<*pref<<std::endl;



참조자는 변수이름에 별칭을 준다고 생각하면 편합니다!



참조자와 함수



C언어를 공부하면서 배운 함수의 두가지 호출 방식은

Call-by-value 그리고 Call-by-reference 입니다.

본래 C언어에서 말하는 Call-by-reference는 주소 값을 전달 받아서,

함수 외부에 선언된 변수에 접근하는 형태의 호출이였습니다.

하지만 C++에서는 함수 외부에 선언된 변수의 접근방법으로 두 가지가 존재합니다.

주소 값을 이용한 Call-by-reference,

참조자를 이용한 Call-by-reference 입니다.

이 둘을 구분하기 위하여 주소 값을 이용한 Call-by-reference는

Call-by-address라고도 합니다~!

어쨌든, 참조자를 이용해서도 함수 외부에 선언된 변수에 접근할 수 있습니다.


void Swap(int &ref1, int &ref2)
{
	int temp=ref1;
	ref1=ref2;
	ref2=temp;
}



함수를 호출하는 방법은 다음과 같습니다


int num1=2;

int num2=3;

Swap(num1,num2);

//참조자는 상수를 참조할 수 없기에 다음과 같은 선언은 컴파일 에러를 발생시킵니다.

Swap(20,30); (X)



const 참조자



포인터는 잘못 사용할 확률이 높고, 참조자의 활용이 상대적으로 포인터의 활용보다 쉽기 때문에,

참조자 기반의 함수정의가 더 좋은 선택이라고 생각할 수 있지만,

참조자 기반의 함수도 단점이 존재한다.

예를들어 함수의 정의나 원형을 보고 함수의 특성을 파악할 수 없습니다.


int num=24;
function(num); 
cout<<num<<endl; //과연 출력 값은??

void function(int prm){ ... } //Call-by-value이기에, num는 변경 안 되므로 24 출력

void function(int &ref){ ... } //이 함수에 의해 num가 바뀔 수도 있습니다



이러한 문제는 const 키워드를 이용하면 어느정도 해결할 수 있습니다.


함수 매개변수 참조자 부분에 const가 붙으면 참조자를 이용한 값의 변경은 불가합니다!

void function(const int &ref){ ... }



참조자에 의해 함수 외부의 변수에 접근할 수 있긴 하지만,

포인터를 사용하는 것이 코드를 더 명확히 작성하는 방법이 된다고 생각하기에,

많은 프로그래머들은 참조자를 잘 사용하지 않습니다.

반환형이 참조자인 경우도 있습니다~


int& Reffunction(int &ref)
{
	return ref;
}

int main(void)
{
	int num1=1;
	
	int &num2=Reffunction(num1); //num2는 num1을 참조합니다.
	
	int num3=Reffunction(num1); //num1의 값 1만 num3에 전달됩니다.
}



그리고 반환형이 참조자일 때는 지역변수를 반환하지 않게 조심해야 합니다.


int& Reffunction(int n)
{
	int num=3;
	
	num+=n; 
    
   return num;
}



위와 같이 선언하면 안 됩니다!!!

지역변수 num는 함수가 종료되면 소멸되기에 함수는 쓰레기 값을 반환하게 됩니다.



마지막으로 상수화된 변수에 대해 참조할 때에도 const를 이용합니다~


const int &ref=50; //상수화된 변수



여기서 50은 상수가 아니라 상수화된 변수입니다~

50을 임시변수에 저장하고 참조자는 그 임시변수를 참조합니다~



malloc & free를 대신하는 new & delete



C언어에서 힙의 메모리 할당 및 소멸에 필요한 함수는 malloc과 free 함수이였습니다!

C++에서는 이를 대체하는 new와 delete라는 키워드가 존재합니다~


int *ptr1=(int *)malloc(sizeof(int)); //malloc함수를 이용한 동적할당

int *ptr2=new int; //new를 이용한 동적 할당



double *arr1=(double *)malloc(sizeof(double)*7); //malloc함수를 이용한 동적할당

double *arr2=new double[7]; //new를 이용한 동적할당



free(prt1);
free(arr1); //free함수를 이용한 동적해제

delete ptr2;
delete []arr2; //delete를 이용한 동적해제



malloc과 free 보다 new와 delete가 훨씬 쉽죠오~~~

객체의 생성에는 반드시 new와 delete를 이용해야 합니다!

이차원 배열의 생성은 다음과 같습니다.


//3*3 이차원 배열의 생성
int **arr=new int[3];
for(int i=0;i<3;i++)
	arr[i]=new int[3];


//delete를 이용한 소멸
for(int i=0;i<3;i++)
	delete []arr[i];
delete arr;



그리고 힙에 할당된 변수는 포인터를 사용하지 않아도 접근할 수 있습니다!


int *ptr=new int;

int &ref=*ptr;

ref=20; //참조자를 이용한 힙에 할당된 변수에 접근



C++에서 C언어의 표준함수 호출하기



C++로 프로그래밍을 하다 보면, C언어의 표준함수를 사용하고 싶을 때가 있습니다.

그 때는 C언어의 헤더파일 선언 시 c를 더하고 .h를 빼면 됩니다.

#include <stdio.h> ->  #include <cstdio>

#include <stdlib.h> -> #include <cstdlib>

#include <math.h> ->   #include <cmath>



예를 들어 0이상 100미만의 난수를 5개 생성하는 예제를 만들어보면,

다음과 같습니다


#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std

int main(void)
{
	srand(time(NULL));
	for(int i=0;i<5;i++)
	{
		printf("난수 %d: %d\n",i+1,rand%100);
	}
}

//printf와 scanf와 같은 표준 함수들은 이미 이름공간 std 내에 선언되어있습니다.