오늘의 TIL 목차 (22.09. 01)
- 이니셜 라이저(feat. const 멤버 변수)
- 클래스 const 멤버 변수/함수
- 클래스 static 멤버 변수/함수
- this
이니셜 라이저
[ 이니셜 라이저 ]
: const 멤버 변수(상수)를 초기화하기 위한 문법 ( 상수 멤버 변수가 없어도 사용됨 )
클래스명::생성자 : 변수명(초기화값) // : 변수명(초기화값)이 이니셜라이저
{
}
-
class CDainn
{
private:
// 이니셜 라이저를 사용하지 않으면 m_ciA는 쓰레기값이 상수화 되어버림
// 생성자 몸체는 '대입을 통한 초기화'라 쓰레기값이 상수화된 상수는 몸체 안에서 초기화 불가능(대입 X)
const int m_ciA;
int m_iB;
public:
CDainn();
~CDainn();
}
// 생성자 정의부
CDainn::CDainn() : m_icA(), m_iB(10) //정적변수, 전역변수는 Data영역의 BSS에 의해 자동 0초기화
{
};
// 기본 생성자 실행 시 m_icA는 0, m_iB는 10으로 초기화
※ 생성자에서 이니셜 라이저 사용 시 상수 변수를 초기화하지 않으면 실행 오류
■ 기능 / 주의
[객체 생성 순서]
1. 메모리 할당
2. 이니셜 라이저 실행 (생략 가능)
3. 생성자 몸체 코드 실행
4. 객체 생성
* 메모리 할당과 생성자 실행 2가지 조건이 충족되어야지만 객체 생성
* C시절의 calloc, malloc, free는 객체를 자동 생성 및 실행하지 않기 떄문에 올바른 객체 생성 X
- 클래스 내에 const 상수가 있으면 무조건 모든 생성자에 이니셜라이저로 const 상수를 초기화해줘야 한다.
- 구조체는 이니셜 라이저 사용 불가능, 포인터는 가능
- 몸체 안에서 memset함수 이용하여 전체 초기화 또는 구조체 멤버 변수 지정하여 대입 - 이니셜라이저는 상수뿐만 아니라 속도가 빠르기 떄문에 변수들을 초기화하는데도 주로 사용된다.
- 생성자 몸체 코드 안의 초기화는 '대입을 통한 초기화', 이니셜 라이저는 '선언과 동시에 초기화'와 유사하다.
- 따라서 상수 멤버 변수는 몸체 코드 안에서 불가(이니셜 라이저로만 가능)하고 이니셜라이저의 속도가 훨씬 빠르다. - 생성자의 매개변수로 바로 초기화가 가능하다. ( ex. Dainn::Dainn(int _iA) : m_iA(_iA) {} )
■ 예시
읽기 전용 함수 ( =const 멤버 함수 )
[ 읽기 전용 함수 ( =const 멤버 함수 ) ]
: const 멤버 함수로 멤버 변수를 읽기만 가능(쓰기 불가능)하게 하는 함수
class CDainn
{
public:
반환타입 함수명(매개변수) const;
};
반환타입 함수명(매개변수) const // 읽기 전용 함수
{
몸체 코드
}
■ 기능 / 주의
- const 함수는 클래스 내의 멤버로서만 존재
- const 함수 오버로딩이 가능 ( 외부 함수에서 클래스 객체 생성 시 const 클래스 객체로 생성하면 const 함수 실행 )
- const 함수 내에선 const 함수만 사용 가능(일반 멤버함수 사용 불가능), 일반 함수내에선 const 함수 사용 가능
- const 클래스 객체는 클래스 내의 const 함수만 실행 가능(일반 멤버함수 실행 불가능)
- 읽기 전용 함수 내에 선언된 변수는 대입 가능(cin도 가능), 멤버 변수만 대입 등 쓰기 불가능 (읽기만 가능)
■ 예시
- string, strcpy_s(), memset(), 이니셜 라이저, const 멤버 함수 오버로딩 및 읽기 전용 함수 다 응용
#include "stdafx.h"
#include <string> // string 함수 사용 시 헤더 포함
struct tInfo
{
char szName[10];
int iA;
const int ciA;
string sString;
tInfo() : ciA(0) {}; // const 변수는 이니셜 라이저로 초기화 필수 (안해주면 기본 생성자 없어서 실행 불가능)
};
class cDainn
{
tInfo m_tInfo;
const int m_ciX;
float m_fiY;
public: // 생성자 및 소멸자 (선언 안해줘도 됨, 자동 실행되기 때문 but 선언 시 정의부까지 꼭 써줘야함)
cDainn();
~cDainn();
public:
void Initialize();
public:
void Render();
void Render() const;
};
// 기본 생성자
cDainn::cDainn() : m_ciX(100), m_fiY(10.f) // 생성자에서는 객체 초기화만
{
// ZeroMemory(&m_tInfo,sizeof(m_tInfo)) // #include <windows.h> 헤더파일 포함 시 가능
memset(&m_tInfo, 0, sizeof(m_tInfo)); // 구조체 초기화
}
// 소멸자
cDainn::~cDainn()
{
}
// 초기화 함수
void cDainn::Initialize()
{
char sCopy[10];
cout << "문자열을 입력하시오: ";
cin >> m_tInfo.sString;
// 구조체 문자열 멤버 변수에 string문자열 대입
// char 문자 배열에 string 문자열 대입 시 c_str()함수를 이용해 string을 char로 변환해야함
strcpy_s(m_tInfo.szName, sizeof(m_tInfo.sString), m_tInfo.sString.c_str());
m_tInfo.iA = m_ciX; // 구조체 멤버 변수 iA에 상수 멤버변수 값 대입
++m_tInfo.iA; // iA 1 증가
}
// 출력 함수
void cDainn::Render()
{
Initialize();
cout << "[일반 Render 함수]" << endl;
int iTemp = 0;
cout << "숫자를 입력하시오: ";
cin >> iTemp;
cout << "구조체 이름 : " << m_tInfo.szName << endl;
iTemp += m_ciX;
cout << "입력 받은 값 + const 멤버 변수 = " << iTemp << endl;
}
void cDainn::Render() const
{
//Initialize(); // 읽기 전용 함수 내에선 읽기 전용 함수만 참조 가능
cout << "[const Render 함수]" << endl;
int iTemp = 0;
cout << "숫자를 입력하시오: ";
cin >> iTemp;
cout << "구조체 이름 : " << m_tInfo.szName << endl; // Initialize에서 값 안 받았으니 memset한 것에 따라 '널' 출력
iTemp += m_ciX;
cout << "입력 받은 값 + const 멤버 변수 = " << iTemp << endl;
}
void main(void)
{
cDainn cD; // 기본 생성자 클래스 객체
const cDainn CCD; // 읽기 전용 함수만 불러오는 기본생성자 const 클래스 객체
cD.Render(); // 일반 Render 함수 실행
//CCD.Initialize(); // const 클래스 객체는 const 멤버 함수만 불러오기 가능
CCD.Render(); // const Render 함수 실행
}
※ const가 붙은 것은 합리적이든 비합리적이든 프로그래머 사이에서 건들지 않기로 약속한 것
static 변수 / 함수 ( = 클래스 변수 / 함수 )
[ 클래스 변수 ]
: static 변수는 함수 및 사용자 정의 자료형(구조체, 클래스) 내에서만 사용할 수 있는 지역 변수의 특성을 가지면서,
데이터는 Data 영역에 저장되어 프로그램 종료 시까지 소멸되지 않는 전역 변수의 특성을 가지고 있다.
: 클래스 내의 static 변수를 클래스 변수라고 하며, 클래스 내부에 선언되어 있지만 멤버 변수가 아닌 독립적인 존재이다.
class 클래스명
{
static 자료형 클래스변수명; // 클래스 변수는 멤버 변수 X
}
//초기화 방법
자료형 클래스명::클래스변수(초기화값);
-
class cDainn
{
private:
static int _isA;
}
// int cDainn::_isA(0);과 동일
int cDainn::_isA; // 정적 변수 및 전역 변수는 Data의 Bss 영역에 의해 자동 0 초기화됨
■ 기능 / 주의
- 클래스 변수는 클래스와 개별적인 존재로 class의 크기에도 포함되지 않는다.
- class와 객체 생성 시점, 공간 할당 위치, 메모리 할당 시점이 다르다. - 클래스 변수는 외부에서 초기화를 해줘야된다. ( 클래스 내부에서 불가능 )
- 멤버 변수가 아니기 때문, 클래스 변수는 존재 여부를 알려주는 역할만 한다.
class CObj
{
private:
static int m_siA;
};
sizeof(CObj); 크기는 1byte ( 클래스 변수 미포함 )
- class는 몸체에 아무것도 없어도 1byte를 기본으로 가진다.
[ 클래스 함수 ]
: 클래스 내의 static 함수를 클래스 함수라고 부르며, class와 개별적인 존재이다. (멤버 함수X)
class 클래스명
{
public:
static Sum(int _iA) {return ++_iA};
}
※ 클래스 함수 내에서는 클래스 멤버 변수 및 함수 사용 불가능, 클래스 변수는 사용 가능
■ 기능 / 주의
- 클래스 함수 내에서는 클래스 멤버 변수 및 멤버 함수 사용 불가능, 클래스 변수 및 함수는 사용 가능
- 클래스와 메모리 할당 시점이 다르기 때문 - 클래스 함수는 외부 함수에서 호출할 때 클래스 객체 없이 소속을 밝혀 불러올 수 있다.
- 클래스명::클래스함수 ( 멤버 함수처럼 '클래스객체.클래스함수' 도 가능)
[ 클래스 함수 호출 방법 ]
cDainn 클래스가 있다고 할 때,
1. 일반적인 멤버 함수 호출 방법
클래스 객체 선언 cDainn cD;
멤버변수 호출 방법 cD.클래스함수명();
2. 소속을 밝혀 호출하기 - 클래스 객체 선언 안해도 됨
cDainn::클래스함수명(); // 멤버 함수가 아니기 때문에 가능
■ 예시
1. static 함수는 멤버 함수 호출 불가 및 클래스 객체 없어 소속으로 호출 가능
#include "stdafx.h"
class cDainn
{
private:
static int siA; // static은 멤버 변수가 아니므로 객체 할당 시점이 달라서 존재 여부만 알려주는 용도
int m_iB;
public:
cDainn() : m_iB(100)
{
//siA = 10;
}
public: // 반환 함수
public:
void Render(int _iA)
{
cout << siA << "\t" << m_iB << endl;
}
static void Render()
{
cout << "값 : ";
}
static void Plus() // 클래스 함수 내에서는 멤버 변수/함수 사용 불가능
{
Render(); // 함수 오버로딩에 의해 static Render 실행, 클래스 함수 사용 가능
//Render(10); // 멤버 함수 사용 불가능
cout << ++siA << endl; // 클래스 변수 사용 가능
//cout << m_iB << endl; // 멤버 변수 사용 불가능
}
};
// 정적변수와 전역변수는 초기화하지 않아도 Data의 Bss에 의해 자동 0 초기화
// static 변수는 멤버 변수가 아니기 때문에 외부에서 초기화해야됨
// static 객체 생성 후 class 객체 생성되는 듯 (그래서 class 생성자에 siA=10하면 10이 출력됨) 그래도 쓰지마
int cDainn::siA;
void main(void)
{
cDainn cD;
cD.Plus();
cDainn::Plus(); // 클래스 변수/함수는 멤버가 아니기 때문에 소속:: 으로 호출 가능
//cout << cD.m_iB << endl; //private이라 이렇게 못해, Get_함수 선언해서 return으로 해
}
2. 각 개별된 같은 클래스 타입의 동적 배열이 static 변수의 값을 전역변수처럼 이어서 사용
#include "stdafx.h"
struct tInfo
{
char szName[10];
int iA; // 문자 배열 인덱스
};
class CObj
{
private:
int m_iA; // 문자열에 대입할 아스키코드값
static int s; // 문자열에 대입할 아스키코드값 static 타입
tInfo _stC;
// _stC대신 _stD를 사용했다면 cobj[1 - 2]에 따로 대입하지 않아도
// cobj[0]에서 대입한 값이 Data영역에 남아있어 [1 - 2]에도 szName에 값이 존재할 것임
// _stC의 경우 각각의 동적배열(힙 메모리)에서 개별로 값을 가지기 때문에 초기값인 널이 존재.
static tInfo _stD;
public: // 생성자
CObj() : m_iA(65), _stC() // 구조체도 초기화됨
{
}
public:
void Initialize();
void Static();
void Zero();
public:
void Render();
static void sRender();
};
tInfo CObj::_stD = {}; // static 구조체 초기화 ( ={}; 안해줘도 자동 0 초기화)
int CObj::s(65); // 문자열에 대입할 값, 65 == A
void CObj::Initialize() // 구조체 문자열 szName에 알파벳 대입해주는 멤버 함수
{
// 클래스 내 함수에서 멤버 변수 값 변경 시 함수가 종료되어도 소멸이 아닌 클래스 내에 그대로 유지된다.
// cobj[0 - 1 - 2]마다 0번째부터 채워지는 이유는 애초에 처음부터 3개의 cobj가 개별로 존재
// 구조체 _stC의 문자열 szName에 값 채우기
cout << "Render" << endl;
for (; _stC.iA < 10; ++_stC.iA) // 구조체의 문자열의 크기까지 증가하며 대입
{
_stC.szName[_stC.iA] = m_iA; // m_iA는 65로 알파벳 대입
cout << _stC.szName[_stC.iA] << "\t";
++m_iA; // 문자열에 A부터 1씩 증가하며차례로 대입
}
cout << endl;
// 함수가 종료되면 m_iA는 소멸되기 때문에 기존 m_iA인 65임
}
void CObj::Static() // Initialize()와 동일한데 알파벳을 담당하는 변수가 static 변수(클래스 변수)
{
// 구조체 _stC의 문자열에 값 채우기
// ???? 여기는 _stC.iA와 m_iA가 0으로 초기화 안되고 10, 75 유지
cout << "Render" << endl;
//m_iA = 65; // 아스키코드로 65는 A
for (; _stC.iA < sizeof(_strC.szName); ++_stC.iA) // 문자열의 크기만큼 대입
{
_stC.szName[_stC.iA] = s; // 클래스 변수(s=65인 상태)로 알파벳 대입
cout << _stC.szName[_stC.iA] << "\t";
++s; // 클래스변수(static 변수)라 다른 CObj 타입 동적 배열에서도 기존의 값이 이어서 들어감.
}
cout << endl;
}
void CObj::Zero() // 메모리 초기화 함수
{
memset(&_stC.szName, 0, sizeof(_stC.szName));
//_stC.iA = 0;
}
void main(void)
{
CObj* cobj = new CObj[3]; // 클래스 CObj 타입 동적 배열 3개 생성
cout << "[ 일반적인 동적 배열 ]" << endl;
cobj[0].Initialize();
cobj[1].Initialize();
cobj[2].Initialize();
/*
for(int i = 0; i<3; ++i) // 각 동적 배열의 문자열 초기화
cobj[i].Zero();
// 각 동적 배열 안의 멤버 변수 값이 그대로 유지되어 있기 때문에
// 현재 _stC.iA와 m_iA는 10, 75인채로 이어서 될거임.
// 근데 문자열 대입하는 함수 조건이 _stc.iA<10까지 넣는거라 문자열에 값이 안 넣어질 뿐
/*cobj[0].Initialize();
cobj[1].Static();
cobj[2].Static();
*/
// 아예 개별적인 cobj배열(cobj 클래스 구조만 같을 뿐)로 존재(포인터도 각각 있음)
// 이때 당연히 각 배열들은 기본 생성자에 의한 초기값을 갖고 있는데
// static 변수는 배열이 개별적인 존재임에도 불구하고 해당 변수는 공용으로 이어서 사용하는 것
cout << "[ static변수 사용 ]" << endl;
cobj[3].Static();
cobj[4].Static();
cobj[5].Static();
delete[] cobj; // 동적 할당은 꼭 해제해줄 것
cobj = nullptr; // dangling 포인터 발생을 방지하기 위해 nullptr 대입
}
this
[ this ]
: 객체의 주소를 저장한 포인터 변수 ( 자기 자신을 가리킴 )
■ 기능 / 주의
- this는 객체의 주소를 담는 포인터로, 객체 밖의 외부 공간에 메모리가 할당되어 있다.
- 모든 매개변수에는 보이지 않지만 this 포인터를 받게 되어있다. ( 어디서든 this 사용 가능 )
- this는 멤버 함수 내에서만 사용 가능하다. ( 자기 자신을 알아야 하기 때문 )
class CDainn
{
CDainn& THIS() // CDainn&으로 반환하여 복사 생성자 일어나지 않도록 함
{
return *this; // this는 자기자신의 주소이므로 *this는 CDainn 그 자체
}
void Print() { cout << this << endl; }
}
void main(void) {
CDainn cd;
cout << &cd << endl; // &는 cd의 주소, 둘 다 주소 동일
cd.Print(); // this는 cd 객체 주소
cd1.THIS().Print(); // THIS()로 자기자신을 반환하였으니 멤버 Print() 호출 가능
}
출력 결과 : 셋 다 똑같은 CDainn 객체 cd의 주소 ( this는 자기자신 주소이므로 )'C++' 카테고리의 다른 글
| [TIL 23장] 상속(feat. UML), 객체 포인터, 바인딩(정적 & 동적), 다형성[클래스 속성], 가상함수, 오버라이딩[다형성특징] (0) | 2022.09.06 |
|---|---|
| [TIL 22장] 동적할당(깊은 복사, 얕은 복사), 복사 생성자, extern, friend (0) | 2022.09.05 |
| [TIL 20장] string함수, explicit, 전방 선언 (0) | 2022.08.31 |
| [TIL 19장] 클래스 특성 - 캡슐화, 클래스 - 생성자, 소멸자 (0) | 2022.08.30 |
| [TIL 18장] OOP의 개념(+추상화), 클래스(4대 속성), 클래스 속성 - 은닉화(feat. 접근 제어 지시자), 파일 분리 (클래스 - 선언부 구현부) (0) | 2022.08.29 |