C++에 중요한 특징인 객체지향 이라는 것이 무엇인지 알아보자
C의 절차지향과 C++의 객체지향이 차이를 코드를 통해 알아보자
//제작자 코드
typedef struct UserData
{
int age;
char name[10];
}UserData;
//사용자 코드
int main(void)
{
UserData USER = { 20,"철수" };
printf("%d,%s", USER.age, USER.name);
return 0;
}
일반적인 C 예제 이다.
지금부터 자신을 완전히 사용자에 입장에서 생각해 보자
나는 유저데이터를 이용해야 한다
유저데이터를 사용하기 위해서는 유저데이터 안에 나이와 이름이 들어있다는 사실을 알아야 하고
각각의 데이터의 유형에 맞춰서 출력해 줘야한다.
사용자가 사용하기에 굉장히 번거롭다
이제 C++ 코드를 살펴보자
//제작자 코드
class UserData
{
public:
int age;
char name[10];
void Print()
{
printf("%d,%s",age,name);
}
};
//사용자 코드
int main(void)
{
UserData USER = { 20,"철수" };
USER.Print();
return 0;
}
C와 출력되는 결과는 같다 하지만 사용자 입장에서 볼때
직관적이고 편리하다
코드에 유지보수에 관점에서 직관성과 편리함은 굉장히 중요하다 그렇기에 우리가
C++에 객체지향 프로그램에 중요성을 공부한다
생성자와 소멸자는 클래스 객체가 생성 및 소멸될 떄 자동적으로 호출되는 함수이다
class MyClass
{
public:
MyClass() //생성자
{
}
~MyClass() //소멸자
{
}
};
기본적으로 클래스를 선언 하면 다음과 같이 클래스는 생성자와 소멸자를 디폴트로 가지고 있다.
그렇다면 생성자와 소멸자가 각각 어떻게 호출되는지 살펴보자
//제작자 코드
class MyClass
{
public:
MyClass() //생성자
{
cout << "생성자 호출" << endl;
}
~MyClass() //소멸자
{
cout << "소멸자 호출" << endl;
}
};
//사용자 코드
int main(void)
{
cout << "시작" << endl;
MyClass a;
cout << "끝" << endl;
return 0;
}
실행결과
시작
생성자 호출
끝
소멸자 호출
생성자는 클래스 객체가 생성될때 호출되고 소멸자는 선언된 블럭 범위가 끝나면 호출된다
소멸자에 호출에 대해 조금 더 알아보자
기본적으로 main함수에서 클래스 객체를 생성하면 객체 또한 지역 변수다
그렇기에 지역변수에 특성상 선언 블럭범위에서만 값이 유호하기에 범위를 끝나는 시점에서 소멸자가 호출된다.
생성자 호출자의 특징 정리
클래스 인스턴스는 new 연산자를 사용해 동적으로 생성 소멸할 수 있다.
int main(void)
{
MyClass* a = new MyClass;
delete a;
MyClass* b = new MyClass[3];
delete[] b;
return 0;
}
동적으로 생성과 소멸시 조금 더 직관적으로 생성자와 소멸자에 호출 시점을 확인할 수 있다.
물론 동적으로 할당한 메모리를 해제 해야한다
오늘 공부한 3장 내용에 핵심이라고 할수있는 생성자 다중 정의 이다
앞서 잠깐 설명한것처럼 생성자는 다중 정의가 가능하다
add(int a) //생성자_1
{
cout << a << endl;
}
add(int a , int b) //생성자_2
{
cout << a + b << endl;
}
int main(void)
{
add num_1(3);
add num_2(3, 4);
return 0;
}
출력 결과
3
7
add 클래스의 생성자가 2개로 다중정의 되있다
그렇기에 사용자가 클래스 객체를 다양하게 생성해서 원하는 값을 출력할수 있다
메서드의 사전적 정의는 방법이다 하지만 이러한 설명은 다소 재미가 없으니 비유를 통해 말하자면
인간이라는 객체가 메서드 달리기,걷기,점프,던지기 등등 여러가지 가지고 있는것이다.
객체가 수행할 수 있는 다양한 기능이라고 생각하자.
this 포인터는 작성 중인 클래스의 인스턴스에 대한 주소를 가리킨다
//제작자 코드
class UserData
{
public:
int age;
char name[10];
void Print()
{
printf("%d,%s",age,name);
}
};
Print()메서드 안에서 this 포인터가 작동한다.
다만 우리눈에 보이지 않을 뿐이다
void Print()
{
printf("%d,%s", this->age, this->name);
}
사실 Print()함수는 this가 포함된 위와같은 코드로 존재한다
우리가 UserData 클래스에 인스턴스를 하나만들면 인스턴스 내에서 this 포인터는
인스턴스 자기 스스로의 주소를 나타낸다는 뜻이다
그렇기에 똑같은 클래스 인스턴스 라도 this 포인터의 값은 다르다
상수형 메서드는 형식이 간단하다
클래스 뒤에 const 형한정어만 붙여주면 된다.
상수형 메서드는 읽기만 가능하고 쓰기는 불가능 한데 쉽게 설명해서
GetData()와 같이 단순히 값을 반환하는 메서드에 사용하면 된다
하지만 add()같이 값을 더하는 메서드에는 사용하면 쓰기가 불가능 하다
const를 얼마나 잘쓰냐가 고수와 하수를 가른다.
정적 멤버는 전역 변수에 가깝다.
전역변수는 특정 인스턴스를 선언하지 않아도 직접 접근 가능한것 처럼
정적 멤버들도 클래스 인스턴스 없이도 직접 접근이 가능하다