클래스 응용

킴스코딩클럽·2022년 11월 23일
1

CS기초 시리즈

목록 보기
55/71

변수의 선언과 정의

int myValue; ------ void Dosomething();
myValue = 1; ------ void Dosomething(){ ~~ }

int myValue{1};

클래스의 선언과 정의

//클래스 선언
class monster
{
private: //접근 제한자
	int hp;
	int mp;
public:
	void attack();
	void Initialize(int hp, int mp) {
		hp = 1;
		mp = 2;
	}
};


int main()
{
	monster wolf; //클래스로부터 인스턴스를 만든다(몬스터 틀에서 울프를 찍어냄)
	wolf.hp; //정의는 하였으나 외부에서 값을 초기화 할수 없음 (캡슐화되어있기 때문)
	wolf.Initialize(1,2); //이걸로 초기화 가능함 하지만 데이터가 많아지면 귀찮아짐
	
	int x {1}; //어떤 상황에서도 다 되는것이 유니폼 초기화

	monster wolf {1,1};
	//private으로 숨겨져 있어 외부에서 보기에는 변수가 없는 것처럼 보여 불가능함
	//public인 변수들에만 작동하게됨

}

생성자와 소멸자

constructor(생성자)

  • 내가 나를 만들어냄
  • 클래스에서 인스턴스가 생성될 때 작동
  • 특별한 멤버함수(정확히는 멤버함수는 아니지만 이해하기 쉽도록)
  • 클래스와 동일한 이름이어야함 (대소문자 모두 일치)
  • 반환타입이 없음( void 쓰지 않음)

destructor(소멸자)

  • 내가 나를 파괴함
  • 인스턴스가 사라질 때
  • 특별한 멤버함수
  • 클래스와 동일한 이름( 대소문자 모두 일치 )
  • 반환 타입 없음( void 쓰지 않음 )
  • 매개변수도 없음
  • ~(tilde) 가 접두어로 붙음

예시

class MyClass
{
	int mValue;
public:
	MyClass(int value);
	~MyClass();
	//생성자와 소멸자는 외부에서 접근 가능하도록 public권한을 줘야함
private:

};

MyClass::MyClass(int value)//매개변수 가능
{
	//constructor
	mValue = value;

	std::cout << "[construct]" << std::endl;
}

MyClass::~MyClass()//매개변수 불가능
{
	//destructor
	std::cout << "[destruct]" << std::endl;
	//자동으로 불리는 것임을 출력을 통해서 확인가능함
}


int main() 
{
	int x = 1;

	MyClass cl(1);
	//인스턴스가 생성된다는 자동으로 생성자가 호출됨 this에 &c1이 들어가게됨(생성된다는 뜻)
	//생성자와 소멸자는 만들어지고 없어질때 자동으로 불러와짐
	//생성자를 불러오기 위해서는 매개변수를 넘겨줘야함
	

}

생성자와 소멸자는 각각 자동으로 호출되는 함수

Quest *q2 = new Quest;
	{
		Quest q3;
	}
	delete q2;
	//q2 construct q3 construct q3 destruct q3 desturct 불리는 순서

기본 생성자

  • 값을 초기화하는 용도로만 쓰이는 것이 주목적
  • 목적은 멤버 변수들의 초기화(모두 초기화 시켜서 사용하기)
  • 함수를 부르는 방식과 동일하지만 자동으로 처리됨
  • 생략 가능함-> 암시적 기본 생성자 (implicit default constructor)
  • 기본 생성자 : 생성자의 역할을 그대로 수행하는 것(멤버 변수들의 기본값-Default value를 지정함)
  • default constructor는 매개변수가 없는 생성자
  • 매개변수가 없는 기본 생성자는 자동으로 만들어지고 아무일도 하지않음
  • 프로그래머가 생성자를 제공하지 않을때만 만들어짐
  • 인스턴스는 여러가지 만들어 낼 수 있지만 기본생성자는 하나라도 만들어내면 더이상 제공되지 않음
  • 함수의 특성을 그대로 물려받아서 사용하는 것이 까다로움(오버로딩 모호성 등등)
class Quest
{
	int mID;
	int mEXP;
	

public:
	Quest();
	~Quest();
	
	void Print()
	{
		std::cout << 
			"QuestID:" << mID << std::endl <<
			"EXP:" << mEXP << std::endl;
	}

};

Quest::Quest()
{
	mID = 1;
	mEXP = 1;
}

Quest::~Quest()
{
}

int main()
{
	Quest q1;
	q1.Print();

}
class MyClass
{
public:
	int mValue1 {1};
	int mValue2 {2};
	//default constructor : 매개변수 없음
	MyClass()
	{
		mValue1 = 1;
		mValue2 = 2;
	}
	MyClass(int x, int y) //오버로딩
	//프로그래머가 생성자를 만들어주기 때문에 기본 생성자는 안만들어졌음
    {
		mValue1 = x;
		mValue2 = y;
	}
	~MyClass()
	{

	}

private:

};

int main()
{

}

생성자 초기화 지양

class Quest
{
	//가급적 멤버변수를 만들고 초기화하는것은 지양하기
	//int mID {0};
	//int mEXP {0};
	int mID;
	int mEXP;
public:
	Quest()
	{
		mID = 1;
		mEXP = 1;
	}


	void print()
	{
		std::cout << "Quest:" << mID << std::endl <<
			"exp:" << mEXP << std::endl;
	}

private:

};

int main()
{
	Quest Q1; //인스턴스 생성 -> 생성자가 자동생성됨
	Q1.print();
}

멤버 초기화 리스트(member initialize list)

const int mID2 {1};
	//멤버변수가 상수라면? 인스턴스가 만들어질때 반드시 초기화되어야함
	//생성자라는 기능이 있어서 이런 방식은 사용하지 않음
	//대체제 - > 멤버 초기화식
    
<생성자> : (하나만) <초기화식1>,<초기화식2> .....
{
}
Quest(int id , int exp = 1) : mID2(id),mEXP{exp}

class MyClass
{
public:
	MyClass(int x, int y)
	{
		x = x; //의미없는 코드
		y = y;
		this->x = x; //멤버의 x
		this->y = y; //멤버의 y
		
	}
	MyClass(int x, int y): 
		x(x), y(y) 
		//x는 무조건 멤버(매개변수의 x)->그래서 가능함
	{

	}
	~MyClass();

private:
	int x;
	int y;
};

----생성 순서-----
class ClassA
{
public:
	ClassA() {
		std::cout << "[A]" << std::endl;
	}
	~ClassA();

private:

};

class ClassB
{
public:
	ClassB() {
		std::cout << "[B]" << std::endl;
	}

private:

};

class ClassC
{
public:
	ClassC() : mB{},mA{} //생성과는 관련없음 초기화만 -> B를 먼저 초기화 하려면 아래 생성시 B를 먼저 써야함

	{
		std::cout << "[C]" << std::endl;
	}

private:
	ClassA mA; //생성 순서에 영향
	ClassB mB;
};
ClassC c;//c는 어떤 순서로 만들어지나요? [A]->[C]
	//CLASSC안에 CLASSA가 자리함

delegating constructor(생성자 위임, 대리 생성자)

다른 생성자를 사용해서 생성하는 

<생성자> : <대리생성자>
{

}
Quest():mID {1}, mEXP {1}
{
}


Quest(int id , int exp = 1) : mID2(id),mEXP(exp) 
{
}

Quest(): Quest(1, 1) 
//대리 생성자
{

}
1. 일반화된 생성자를 특수 생성자가 사용가능하지만 반대는 불가능
2. 더 넓은 의미가 대리가 가능함
3. 쌓아놓고 빠져나오는 스택처럼 생성

소멸자

1. 기본 목적은 인스턴스가 사용한 메모리를 해제
2. 동적 메모리 관리에서 소멸자가 매우 중요하게 작동
class MyClass2
{
private:
	int mSize;
	int *mArray;
public:
	MyClass2(int size): mSize {size}
	{
		mArray = new int[size];
	}
	~MyClass2(); 
	//소멸자
	//매개변수가 없어서 간단함
	//초기화관련 문법 필요없음
	//대부분 해야할 일이 별로 없음

	
};

MyClass2::~MyClass2()
{
	std::cout << "[D]" << std::endl;
	//동적 메모리를 할당하였다면 delete가 여기서 
	delete[] mArray;
}

int main() {
	MyClass2 c1(10); //[C]->[D]
}

모던 c++ (11~)

myclass() = default;

class MyClass
{
public:
	MyClass() = default; 
    //기본생성자임을 알려줌
};

POD ( plain old data ) : 간단하고 안정적으로 오래된 데이터

다양한 곳으로 전송하려다 보니 메모리 복사나 이동이 쉬운 방법이 필요하게됨 -> 이 심플한 구조를 POD라고 부름

std::cout<<std::is_trivial<int>::value<<
":"<<std::is_pod<int>::value<<std::endl;

구조체나 다른 데이터 타입은 메모리 올라가 있는 정보 그대로
하지만 생성자는 전송하려면 특별한 규칙이 필요함(데이터 타입 전송이 더 필요함) 그래서 생성자를 제공하면 trivial하지않고 pod하지 않음 하지만 MyClass() = default; 는 pod로 인정해줌

유니폼 초기화식

class Quest
{
	int mID;
	int mEXP;
public:

	//기본생성자
	Quest()
	{
		mID = 1;
		mEXP = 1;
	}

	//Quest(int id=1,int exp=1) 
		//이러면 q1이 판단할 수 없음 생략된 것인지 두개 다 들어간것인지 모름->주의
		//모호성
	Quest(int id , int exp = 1)
	{
		mID = id;
		mEXP = exp;
	}

	void print()
	{
		std::cout << "Quest:" << mID << "\t" <<
			"exp:" << mEXP << std::endl;
	}

};
int main()
{
	Quest Q1;
	//인스턴스 생성 -> 생성자가 자동생성됨 -> 1,1
	Quest q1();
	//quest클래스로 q1인스턴스를 기본생성자로 만드는 것인지 
	//quest를 반환하는 매개변수가 없는 함수 q1의 선언인지 혼동하기 쉬움
	Quest q1 {}; //유니폼 초기화 식으로 가능

	Quest Q2(2, 100);
	Quest Q2{2, 100};
	//클래스의 인스턴스를 만들때 유니폼 초기화식이 하는 일은 생성자 호출이다

	Quest q3(3);

	Q1.print();//1,1
	Q2.print();//2,100
	q3.print();//3,1
}

초기화 개념 정리


int x = 1; 	: 복사 초기화(copy initial~)
//int x = int();와 같음

int x (2);	: 직접 초기화(direct initial~)

int x {3};	: 유니폼 초기화(uniform initial~)

//int x = int(4);	: 유니폼 복사 초기화(uniform copy inital~)
int x = {4};

Quest q5 = Quest();

Quest q6(2, 100);
//생성자를 통해서 직접 초기화

Quest q7 {3,200};

Quest q8 = {4,300};
//Quest{4,300};과 같음 Quest q8이 만들어지고 거기에 복사됨

개념상 복사가 들어가지 않으므로 유니폼 초기화나 직접 초기화가 빠름

int x = 1;  을 빠르게 하려면 -> 복사를 하지말고 r-value(1)에 이름을 붙이기

Quest c1 = Quest(1); 
	Quest c2 = {1}; //rvalue를 만들고 c2라는 이름을 붙임(복사의 개념이 아니라 이름만 붙여서
    
myClass *p{};
//마이 클래스를 가리키는 포인터 p를 nullptr로 초기화
myClass *p {new myClass{}}; 
myClass *p {new myClass}; 
//여기서 myClass{}는 기본생성자
//*ㅔ{}는 마이클래스 포인터에 대입하는 것을 대체
//유니폼 초기화식을 사용시 어떤것을 대체되는 것인지 확인해야함
//myclass를 유니폼 초기화한 동적 메모리가 할당된

myClass(1); 
//마이클래스의 인스턴스를 만들었는데 이름이 없음(rvalue)  
//아무의미없는 코드
1;
int(1);
//위 세가지가 같은 의미

visual studio

비주얼 스튜디오 개별 구성 요소

  • class designer
  • 클래스 다이어그램 보기
  • 끌어와서 보기 (필드 , 메서드)
  • 서버정보, 로그인 정보, 유닛, 마우스 클릭 정보 등등 모든 정보들이 클래스가 되고 상관관계를 파악하기 쉬워지게됨
profile
공부 기록용

0개의 댓글