변수의 선언과 정의
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인 변수들에만 작동하게됨
}
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 불리는 순서
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();
}
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가 자리함
다른 생성자를 사용해서 생성하는
<생성자> : <대리생성자>
{
}
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]
}
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);
//위 세가지가 같은 의미
비주얼 스튜디오 개별 구성 요소