본문으로 바로가기

[C++] C언어의 확장

category 개발자과정준비/C++ 2021. 5. 28. 18:28
반응형

C++ 배경

옛날에는 소규모, 개인 프로젝트라서 C언어로도 충분히 프로젝트를 진행할 수 있었지만,

산업이 발전하면서 여러가지 형태의 응용프로그램들이 나타나다보니 C의 한계가 드러나기 시작했다.

그래서 C를 발전시켜서 C++/ C++++(=C#) 이 탄생하게되었다.

 

C는 절차지향 -> 한줄씩 단계별로 컴파일

C++은 객체지향 -> 부품들을 하나씩 조립하는 형식으로 컴파일

 

 

클래스

C++의 클래스는 C언어의 구조체에서의 개념과 비슷하다.

C에서 구조체는 멤버 변수만 가질 수 있다.

C++ 구조체는 멤버 변수, 멤버 함수를 가질 수 있다. 이것을 클래스라고 부른다.

따라서 클래스는 구조체가 발전된 형태라고 볼 수 있다.

(참고로 C에서의 함수는 객체지향에서 메서드와 같은 개념이라고 볼 수 있다)

 

생성자 : 클래스를 초기화 시키는 친구

소멸자 : 생성자를 소멸시키는 친구

 

객체 지향 언어의 특징

캡슐화 : 묶어놓는 것

추상화 : 나타내는 것(머릿속에 있는것을 컴퓨터로 나타내는 것)

은폐    : 숨기는 것(멤버 접근 지정자를 사용), 보통 멤버 변수가 중요해서 변수를 은폐시킴.

다형성 : 여기서도쓰고 저기서도 쓰는것(오버로딩 이라고도 부름)

상속    : 쉽게말해 재사용임.

 

C언어의 확장

C에 비해서 C++은 어떤 장점을 가지는지 알아보도록하자.

우선 C언어 코드의 확장자를 cpp로 저장해서 예제를 작성해보자.

 

cursor.h 헤더파일 추가

#include <windows.h>
#include <time.h>

#define randomize() srand((unsigned)time(NULL))
#define random(n) (rand() % (n))
#define delay(n) Sleep(n)
#define clrscr() system("cls")
#define gotoxy(x,y) { COORD Cur = {x, y}; \
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),Cur);}
#define showcursor(bShow) { CONSOLE_CURSOR_INFO CurInfo = {20, bShow}; \
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&CurInfo); }

 

randnumOop.cpp 소스파일 추가

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "cursor.h"

class RandNum
{
private:
	int num;

public:
	RandNum() 
	{
		randomize(); 
	}
	void Generate() 
	{ 
		num = random(100) + 1; 
	}
	BOOL Compare(int input) 
	{
		if (input == num) 
		{
			printf("맞췄습니다.\n");
			return TRUE;
		}
		else if (input > num) 
		{
			printf("입력한 숫자보다 더 작습니다.\n");
		}
		else 
		{
			printf("입력한 숫자보다 더 큽니다.\n");
		}
		return FALSE;
	}
};

class Ask
{
private:
	int input;

public:
	void Prompt() 
	{ 
		printf("\n제가 만든 숫자를 맞춰 보세요.\n"); 
	}
	BOOL AskUser() 
	{
		printf("숫자를 입력하세요(끝낼 때는 999) : ");
		scanf("%d", &input);
		if (input == 999) 
		{
			return TRUE;
		}
		return FALSE;
	}
	int GetInput() 
	{ 
		return input; 
	}
};

int main()
{
	RandNum R;
	Ask A;

	for (;;)
	{
		R.Generate();
		A.Prompt();
		for (;;) 
		{
			if (A.AskUser()) 
			{
				exit(0);
			}
			if (R.Compare(A.GetInput()))
			{
				break;
			}
		}
	}
}

숫자를 맞출때까지 출력, 맞췄을때도 무한반복으로 실행, 999를 입력하면 종료

 

해당예제는 객체지향으로 랜덤한 숫자를 사용자가 맞춰보는 예제이다.

객체 지향은 부품 조립식이며 부품이 되는 객체를 얼마나 잘 조립해서 만드느냐가 관건이다.

훌륭한 부품을 만드는것이 어려운 일이지만 한번만 잘 만들어 놓으면 많은 사람이 편리하게 활용할 수 있다.

객체지향의 기본 철학은 재활용성이며, 비슷한 코드를 매번 만들지 말고 딱 한번 제대로 만들어놓고 적극적으로 재활용하는 것이 중요하다.

 

 

bool형

C는 별도의 진위형이 없어서 조건문에 참과 거짓을 판별할때 정수를 대신 사용했다.

거짓은 0으로 확정적이나, 0 이외에는 모두 참이어서 일관되지 못해서 불편하게 사용했었는데,

C++는 bool 타입을 도입하여 true, false로 진위형을 표현할 수 있다.

// 파일명 : bool.cpp
#include <stdio.h>

int main() 
{
	int age = 25;
	bool isAdult = age > 19;
	if (isAdult) 
	{
		puts("성인입니다");
	}
}

확장자만 cpp로 해줬는데도 자료타입을 bool형으로 선언해서 T/F를 사용할 수 있다.

 

태그

C++은 태그를 하나의 타입으로 인정하기때문에 태그명으로 변수를 선언할 수 있다.

 

C는 반드시 태그앞에 enum, struct 키워드를 붙이지만,

C++은 태그명 자체를 타입으로 인정한다.(클래스도 하나의 타입으로 인정한다)

// 파일명 : tagtype.cpp
#include <stdio.h>

int main() 
{
	enum origin {EAST, WEST, SOUTH, NORTH};
	// enum origin mark = WEST;
	origin mark = WEST;
	printf("%d 방향\n", mark);

	struct SHuman
	{
		char name[12];
		int age;
		double height;
	};
	// struct SHuman kim = { "홍길동", 30, 200.4 };
	SHuman kim = { "홍길동", 30, 200.4 };
	
	printf("이름 = %s, 나이 = %d\n", kim.name, kim.age);
}

C에서는 주석처리된 부분처럼

enum origin mark = WEST; 

struct SHuman kim = { "홍길동", 30, 200.4 };  를

붙여줘야하는데 C++에서는 붙이지않아도 일반 변수처럼 사용할 수 있다.

 

 

명시적캐스팅

C 형식 : (타입)변수
C++ 형식 : 타입(변수)

C++은 단일 변수를 캐스팅할때는 별 차이가 없지만 수식을 캐스팅할때는 괄호가 필요없다.

C 형식 : (float)(a+b)
C++형식: float(a+b)

 대신, 함수와 헷갈릴수도있는데, 이름이 기본 자료형이니까 캐스팅을 하고있다는 점을 구분하면 될 것이다.

 

 

new 연산자

 new 연산자는 데이터를 메모리에 할당해주는 연산자이다.

 C에서는    malloc()  -  free()         => 함수

 C++ 에서는   new  - delete          => 연산자

 C++은 연산자이기때문에 별도의 헤더 파일을 포함할 필요가 없다.

 new 연산자는 객체 할당시 생성자를 호출하고, delete 연산자는 객체를 해제할때 소멸자를 호출한다.

 

new 연산자를통한 동적할당 예제

// 파일이름 : newdelete.cpp
#include <stdio.h>

int main() 
{
	int* pi, *pj;

	pi = new int;
	pj = new int(10);    // 생성자를 호출하는 '연산자'이므로 바로 초기화 가능함.

	*pi = 123;           // 할당한 후에 초기화
	
	printf("*pi = %d\n", *pi);
	printf("*pj = %d\n", *pj);

	delete pi;
	delete pj;
}

 C언어 malloc은 할당받은 주소값만 리턴할 수 있고 초기화는 불가능하지만,

 C++ new 연산자는 할당시키면서 초기화가 가능하다는 차이점이있다.

 

우선 C의 동적할당을 살펴보자.

pi = (int*)malloc(sizeof(int));
*pi = 123;
printf("*pi = %d\n", *pi);
free(pi);

 malloc 함수는 바이트 단위로 할당량을 지정하므로 sizeof 연산자가 필요하고 void* 를 리턴하므로 캐스트 연산자도 반드시 사용해야한다.

 

 이에 반해 위의 예제처럼 new 연산자는 할당할 때부터 타입을 지정하므로 크기를 밝힐 필요가 없고 캐스팅할 필요도 없으며, 사용자 정의타입도 할당할 수 있다.

 

또,

C언어는 역참조 에러를 없애기위해 NULL일때(메모리 할당못받으면 NULL을 반환) 조건문을 따로 걸어줬었는데,

C++도 할당에 실패하면 NULL을 리턴하는데, 단일 변수 할당에 실패하는 경우는 거의 없다고 볼 수 있다.

그리고 위의 예제 코드를 실행했을때도 초록줄(경고)가 뜨지않았는데, 그만큼 내부적으로 안정적이라는 점을 알 수 있다.

 

 

배열을 메모리에 동적할당하는 방법

#include <stdio.h>

int main() 
{
	int* ar;

	ar = new int[5];
    // ar = (int*)malloc(sizeof(int) * 5)  // C에서의 배열 동적할당
	for (int i = 0; i < 5; i++) 
	{
		ar[i] = i;
	}
	for (int i = 0; i < 5; i++) 
	{
		printf("%d번째 = %d\n", i, ar[i]);
	}
	delete[] ar;
}

 new int[5] 구문에 의해 정수형 변수 5개를 저장할 수 있는 20바이트의 메모리가 할당되며 그 자리에 포인터 변수로 받으면 정수형 배열이 된다.

 

 배열을 할당할 때는 기본값으로 초기화되며 별도의 초기값을 지정할 필요는 없다.

new[] 연산자로 생성자를 호출했으니 delete[]로 전체 요소를 한꺼번에 해제할 수 있다.

 

 

셀프테스트) 크기 100의 실수형 배열을 할당하고 50번째 요소에 3.14를 대입하여 출력

#include <stdio.h>

int main() 
{
	double* ar;
	ar = new double[100];
	ar[50] = 3.14;
	printf("ar[50] : %.2f \n", ar[50]);
}

 

반응형