const와 클래스

 

const로 멤버 변수 선언 시 선언부에서 바로 초기화가 가능하다.

const int		m_a = 10;

단, 해당 클래스로 만든 모든 객체가 동일한 값을 가지게 된다.

그러므로 const로 멤버 변수 선언 시 초기화를 시키는 방법은 이니셜라이져를 사용하는 것이다.

 

이니셜라이져란?

-> 메모리 할당과 동시에 호출되는 것.

-> 메모리 할당과 동시에 호출되기 때문에 선언과 동시에 멤버 변수 초기화를 할 수 있다.

 

 

이니셜라이져 선언 방법

-> 생성자의 선언부와 정의부 사이에 : (콜론)으로 명시해준다.

class CObj
{
public:
	CObj(int _a, int _b)    
		// 이니셜라이져를 사용한다는 의미		
		: m_a(_a), m_b(_b)
	{		
		m_a = _a;
	}

private:
	const int		m_a;
	const int		m_b;

생성자 호출 순서
객체 생성 -> 메모리 할당 -> 생성자 호출
-> 메모리 할당 당시에 m_a는 쓰레기 값으로 초기화가 진행된다.
-> 이후 생성자에서 대입을 시도하는 것은 선언 후 초기화 형태이다.

 

이니셜라이져와 생성자의 차이점

// 이니셜 라이져
int a = 10;

// 생성자
int a;
a = 10;

 

const와 멤버 함수

-> const의 위치에 따라 상수화 되는 것이 다르다.

#1. 반환 타입 앞에 있는 const

#2. 반환 타입 뒤에 있는 const

 

반환타입 앞에 const가 붙으면 외부에서 수정이 불가능하다.

class CObj
{
public:
	CObj(){}

public:
	const void Const_Func()
	{		
		m_a = 10; // 불가능	
	}

private:
	const int m_a;
};

반환타입 뒤에 const가 붙으면 정의부가 상수가 된다.

-> 정의부 내부에서 멤버 변수들의 값 변경이 불가능하다. 

class CObj
{
public:
	CObj(){}

public:
	void Const_Func() const
	{		
		m_a = 10; // 불가능	
	}

private:
	int m_a;
};

단, 지역 변수들의 값 변경은 가능하다.

class CObj
{
public:
	CObj(){}

public:
	const void Const_Func() const
	{		
		int a = 10;
		a = 20;
	}
private:
	int m_a;
};

 

 const 멤버 함수 내부에서는 일반 멤버 함수 호출이 불가능하다.
 -> 일반 멤버 함수 내부에서 멤버 변수들의 값 변경 시도가 있을 수 있기 때문이다.

class CObj
{
public:
	CObj(){}

public:
	const void Const_Func() const
	{			
		Func();        // 불가능
		Const_Func();  // 가능
	}

	void Func()
	{
		int b = 10;
	}
};

 

const 객체

const CObj	obj;

-> 멤버 변수들이 모두 const화 된다.

-> 즉, 객체가 가지고 있는 멤버들의 값을 변경하지 못한다.

-> 이로 인해, const 멤버 함수만 호출이 가능하다.

-> 일반 멤버함수들의 호출은 불가능하다.

obj.Const_Func(); // 가능
obj.Func();       // 불가능

-> 단, :(이니셜라이져)를 이용한 변수 초기화는 가능하다.

 

원칙적으로 모든 곳에 const를 사용한다. 여기에는 지역 변수와 함수 매개 변수도 포함된다.

-> 개체를 수정하지 않는 멤버 함수에는 모두 const를 붙인다.

int GetNum() const;

값(value) 형식의 변수를 const로 반환하지 않는다. 포인터나 참조(reference)를 반환할 경우에만 const 반환을 한다.

 

단순 상수 변수에는 const 대신 constexpr 을 사용한다.

const int BUFFER_SIZE = 512;     // 적용전

constexpr int BUFFER_SIZE = 512; // 적용후

스트림 개방 함수

fopen_s()

errno_t

->스트림 개방 성공 시 0, 실패 시 이유에 따른 값을 반환

 

FILE** _Stream

-> 스트림을 저장할 변수의 주소를 전달한다.

-> 내부에서 동적할당해주기 때문에 이중 포인터를 사용한다.

 

const char* _FileName

-> 경로와 파일 이름, 확장자까지 전달한다.

 

const char* _Name

-> 읽기 / 쓰기, Text형식 / Binary형식 인지 전달한다.

 

 

스트림 소멸 함수

-> fclose()

 

쓰기

FILE* fp = nullptr;

 

절대 경로

errno_t err = fopen_s(&fp, "C:/Users/Desktop/Data/Text.txt", "wt");

상대 경로

errno_t err = fopen_s(&fp, "../Data/Text.txt", "wt");

 

스트림 개방

if (0 == err)
	{
		fputc('A', fp);
		fputc('B', fp);

		cout << "스트림 개방 성공" << endl;
		fclose(fp);
	}
	else
		cout << "스트림 개방 실패" << endl;

스트림 읽기

// 읽기
	char chA = 0, chB = 0;
	FILE*	fp = nullptr;

	errno_t err = fopen_s(&fp, "../Data/Text.txt", "rt");

	if (0 == err)
	{
		chA = fgetc(fp);
		chB = fgetc(fp);

		cout << "파일 개방 성공" << endl;
		fclose(fp);
	}
	else
		cout << "파일 개방 실패" << endl;

	cout << chA << ", " << chB << endl;

 

입출력 모드

r : Read의 약자. 읽기 모드일 때 사용한다. // 파일이 없을 경우 오류

w : Write의 약자. 쓰기 모드일 때 사용한다. // 파일이 없을 경우 생성

a : Append의 약자. 이어 쓰기 모드일 때 사용한다. // 파일이 없을 경우 생성

+ : 읽기 / 쓰기 모두 사용 가능

-> 좋은 모드라고 생각할 수 있으나, 읽기에서 쓰기로, 쓰기에서 읽기로 작업이 변경될 경우

-> 버퍼를 모두 비워주어야 하는 불편함이 있다.

 

데이터 종류 모드

t : Text의 약자. 사람이 읽고 쓰기 편한 모드.

b : Binary의 약자. 컴퓨터가 일고 쓰기 편한 모드.

 

binary 모드의 장점

#1. 속도가 빠르다.

#2. 그림, 영상, mp3 파일 등 읽기 어려운 파일의 입출력이 자유롭다.

#3. 배열 또는 구조체처럼 연속된 메모리를 사용하는 데이터일 경우 통째로 저장 및 불러오기가 가능하다.

 

binary 모드의 주의 사항

-> 저장하는 데이터 중 주소가 있을 경우 주소는 저장하면 안된다.

 

binary 모드 출력

fwrite()

const void* _Buffer

-> 저장할 메모리의 시작 주소


size_t _ElementSize

-> 얼마만큼 저장할 것인지 크기(byte단위)


size_t _ElementCount

-> 그 것들을 몇 개 저장할 것인지 전달


FILE* _Stream

-> 개방한 스트림을 전달

 

binary 모드 출력

int iArr[5] = { 1, 2, 3, 4, 5 };

FILE* fp = nullptr;
errno_t err = fopen_s(&fp, "../Data/Binary.txt", "wb");
if (0 == err)
{
	fwrite(iArr, sizeof(iArr), 1, fp);
	//fwrite(iArr, sizeof(int), 5, fp);

	cout << "파일 개방 성공" << endl;
	fclose(fp);
}
else
	cout << "파일 개방 실패" << endl;

 

binary 모드 입력

fread()

const void* _Buffer

-> 어떤 메모리에 읽어올 것인지 시작 주소


size_t _ElementSize

-> 얼마만큼 읽어올 것인지 크기를 전달(byte단위)


size_t _ElementCount

-> 몇 개를 읽을 것인지 전달


FILE* _Stream

-> 어떤 스트림을 이용할 것인지 전달

 

int iArr[5] = {};

FILE* fp = nullptr;
errno_t err = fopen_s(&fp, "../Data/Binary.txt", "rb");
if (0 == err)
{
	fread(iArr, sizeof(iArr), 1, fp);
	//fread(iArr, sizeof(int), 5, fp);
    
	cout << "파일 개방 성공" << endl;
	fclose(fp);
}
else
	cout << "파일 개방 실패" << endl;


for (int i = 0; i < 5; ++i)
	cout << iArr[i] << endl;

 

이미지 파일 복사 방법

void main()
{
	FILE*	fp_In = nullptr;
	FILE*	fp_Out = nullptr;

	errno_t err_In = fopen_s(&fp_In, "../Data/Image.jpg", "rb");
	if (0 == err_In)
	{
		//////////////////////////////////////////////////////////////////////
		errno_t err_Out = fopen_s(&fp_Out, "../Data/Copy.jpg", "wb");
		if (0 == err_Out)
		{
			char	ch = 0;
             
                        // 원본 파일의 사이즈 복사
			while (true)
			{
				int iCnt = fread(&ch, sizeof(char), 1, fp_In);

				if (1 > iCnt)
					break;

				fwrite(&ch, sizeof(char), 1, fp_Out);
			}

			cout << "복사 성공" << endl;
			fclose(fp_Out);
		}
		else
			cout << "복사 실패" << endl;
		//////////////////////////////////////////////////////////////////////


		cout << "불러오기 성공" << endl;
		fclose(fp_In);
	}
	else
		cout << "불러오기 실패" << endl;	
}

'Programming > C++ Basic' 카테고리의 다른 글

C++ static과 클래스 , 멤버 함수  (0) 2020.09.15
C++ const 와 클래스, 멤버 함수  (0) 2020.09.15
C++ 전방 선언  (0) 2020.09.13
C++ 생성자와 소멸자, explicit 키워드  (2) 2020.09.13
C++ 메모리 함수  (0) 2020.09.13

전방 선언

-> 외부 파일 어딘가에 해당 클래스가 존재한다고 알려주는 역할

 

다양한 파일들을 만들고 서로 엮는 과정에서 불필요한 파일의 정보까지 가져오는 상황이 발생한다.

이를 해결하기 위해서 전방 선언을 사용하는 것이 좋다.

 

include와 전방 선언의 차이점

 

include

-> 기존에 존재하는 정보를 확인할 수 있다.

 

전방 선언

-> 어딘가에 존재한다는 것을 알 수 있지만 상세한 정보는 알 수 없다.

 

전방 선언의 장점

#1. 파일의 크기가 작아진다.(컴파일 속도의 향상)

 

전방 선언 시 주의 사항

 

#1. 존재만 알려준다.

-> 프로그램에서 전방 선언한 것이 존재하는지 검사를 하지 않는다.

-> 오타가 발생할 경우 찾기 힘들다.

 

#2. 존재만 알기 때문에 객체를 만들 수 없다.

-> 단, 포인터형으로는 사용이 가능하다

 

#3. 객체의 멤버 함수를 사용하는 곳은 .cpp파일이다.

-> .cpp파일에서는 include를 해주어야 한다.

 

사용 예)

A.h      // A 헤더 파일

class B; // 전방 선언
class C; 

class A
{
private:
    B* m_B; // 전방 선언시에는 포인터 변수로만 선언이 가능하다.
    C* m_C;
}

B.h      // B 헤더 파일
class B
{
  코드 블럭;
}

C.h      // C 헤더 파일
class C
{
  코드 블럭;
}

A.cpp     // 소스 파일

#include "B.h" // .cpp 파일에는 전방 선언한 것을 인클루드
#inlcude "C.h" 
class CObj
{
public:
	//생성자의 형태
	CObj()
	{
	    cout << "생성자 호출" << endl;
	}
	CObj(int _a, double _b)
	{
		m_iA = _a;
		m_dB = _b;
		cout << "인자 있는 생성자 호출" << endl;
	}

private:
	int		m_iA;
	double	m_dB;
};

 

생성자

-> 객체 생성 시 자동으로 호출 되는 것.

-> 사용자가 호출하지 않는다.

-> 함수의 일종으로 인자 타입, 인자 개수에 따른 오버로딩이 가능하다.

-> 클래스의 이름으로 정의된다.

-> 반환 타입이 없다.

-> 객체 생성은 외부에서 진행하기 때문에 생성자의 멤버 접근 지정자는 public으로 설정한다.

-> 객체는 생성자에 의해 완성된다.

 

생성자의 주 사용 목적

-> 객체 멤버로 초기화할 값이 있을 경우 사용한다.

 

생성자로 데이터를 전달하는 방법

-> C++ 초기화 형식과 동일하다.

CObj obj(10, 3.14);

 

생성자 사용 시 주의 사항

 

#1. 객체 생성은 생성자 호출에 의해 완성된다.

-> 호출할 생성자가 없을 경우 객체를 만들 수 없다.

-> 오버로딩을 이용하여 생성자를 여러 개 정의해주어야 한다.

CObj	obj1;
CObj	obj2(10, 3.14);

#2. 디폴트 생성자

-> 사용자가 생성자를 정의하지 않을 경우 컴파일러는 디폴트 생성자를 자동으로 생성하여 호출한다.

-> 단, 사용자가 한개 이상의 생성자를 정의할 경우 디폴트 생성자는 생성하지 않는다.

 

explicit 키워드

-> 생성자 앞에  explicit 키워드를 명시하면 C기반의 초기화(형 변환)을 막는다.

class CObj
{
public:
	explicit CObj() {}
	explicit CObj(int _a) { m_a = _a; }

public:
	void Func()
	{
		cout << m_a << endl;
	}

private:
	int		m_a;
};
// C기반의 초기화
CObj	obj2 = 10; // 불가능

-> 초기화는 무조건 C++기반의 초기화만 허용한다.

// C++기반의 초기화
CObj	obj2(10); // 가능

예를 들어 다음과 같이 대입을 하게 되면

CObj	obj3 = 20;

대입 연산자 기준 좌측은 사용자 정의 자료형, 우측은 int형이다.

즉, 타입이 다른 자료형끼리 대입이 진행되고 있다.(초기화가 가능)

 

대입 연산자 기준 좌측 자료형으로 형 변환이 발생했다.

20이라는 int형 데이터가 CObj Temp(20) 이라는 형태로 형 변환이 발생하고,

CObj obj3 = Temp 형식으로 값을 대입하고 있다.

 

형 변환이 발생한다 하여도 사용자가 보기에는 어색함이 있을 수도 있다.

이를 방지하기 위하여 explicit 키워드를 사용한다.

 

소멸자

-> 객체 소멸 시 자동으로 호출되는 것.

-> 생성자와 비슷하지만 반대 개념이다.

-> 사용자가 소멸자를 정의하지 않을 경우 컴파일러가 디폴트 소멸자를 자동으로 생성하여 호출한다.

-> 사용자가 소멸자를 정의하면 디폴트 소멸자는 생성되지 않는다.

-> 객체 소멸 또한 외부에서 진행되기 때문에 public으로 설정해준다.

-> 객체 멤버로 동적할당한 변수가 있을 경우 해제하는 용도로 사용한다.

class CObj
{
public:
	CObj() { cout << "생성자 호출" << endl; }
	~CObj() { cout << "소멸자 호출" << endl; }

};

 

생성자와 소멸자의 호출 순서

객체 생성 -> 메모리 할당 -> 생성자 호출 -> 소멸자 호출 -> 메모리 해제 -> 객체 소멸

'Programming > C++ Basic' 카테고리의 다른 글

C++ 파일 입출력(fopen_s, fread(), fwrite())  (0) 2020.09.13
C++ 전방 선언  (0) 2020.09.13
C++ 메모리 함수  (0) 2020.09.13
C++ 클래스, 클래스의 4대 속성  (1) 2020.09.10
C++ Debug, 조건식 Debug  (0) 2020.09.08

+ Recent posts