구조체에서의 멤버변수는 은닉이 안되서 사용자 층에서 접근이 가능했다.
객체지향으로 넘어오면서 접근제한지정자를 통해 멤버변수를 은닉하여 접근 할 수 없도록 만들 수 있게 되었다.
class Cmy{
//public은 외부에서 접근이 가능하다.
public :
int p_i;
}
int main(){
Cmy c;
//public은 객체에 직접적인 접근을 허용한다.
c.p_i = 30;
return 0;
}
class Cmy{
//private는 외부에서 접근이 불가능하고 해당클래스에서 접근해야한다.
private :
int m_i;
public :
void SetInt(int i) {
//this 생략가능
this->m_i = i;
}
}
int main(){
Cmy c;
//private는 멤버변수의 직접적인 접근을 불허한다.
//c.m_i = 30; // 컴파일 에러
c.SetInt(20); //private멤버변수를 함수를 통해 가져올 수 있다.
return 0;
}
클래스를 인스턴스화 할 때 생성된 객체를 초기화할 수 있도록 해준다.
만약 사용자가 클래스 생성을 할 때 생성자를 정의하지 않은면 default 생성자가 생성된다.
생성자 작성은 다음과 같이 한다.
class Cmy{
private:
int m_i;
float m_f;
public:
Cmy()
:m_i(100), m_f(0.1f){
}
}
메인 함수에서 클래스를 인스턴스화 하면 자동으로 생성된다.
클래스를 통해 생성된 객체는 힙영역의 메모리 영역을 차지하는데 사용된 객체를 메모리에서 해제시켜주는 소멸자가 있다.
#include <iostream>
class Cmy {
private:
int m_i;
float m_f;
public :
int p_i;
//private 클래스 내부에서 접근 가능
void SetInt(int i) {
//this 생략가능
this->m_i = i;
}
public :
//생성자
//(이니셜라이져)
//생성자와 소멸자를 작성하지 않으면 클래스 생성 시 default 생성자, 소멸자로 생성된다.
Cmy()
: m_i(100)
, m_f(0.f)
{
}
//소멸자
// 사용된 객체들을 해제해준다.
~Cmy() {
}
};
int main() {
Cmy c;
//멤버함수는 호출할 때 객체가 필요하다.
// 본인 객체의 주소를 포인팅하는 것을 this 포인터
// 맴버함수를 객체를 통해 호출하면, 해당 객체의 주소가 this 포인터로 전달된다.
c.SetInt(20);
return 0;
}
대입 연산자를 이해하려면 래퍼런스 변수를 짚고 넘어가야 한다.
래퍼런스 변수란 C++에서 추가된 기능으로 포인터와 유사한 기능한다.
포인터에서는 포인터 변수를 선언하고 일반자료형의 데이터같은 경우 &을 붙여서 엠퍼센트 변수명을 이용하여 주소값을 포인터 변수에 할당한다.
포인터 변수를 이를 활용하여 일반자료형의 원본데이터를 수정할 수 있고 const를 붙여서 수정을 제한적으로 할 수 도 있다.
포인터 변수로 만든 값들은 아스타를 붙여서 사용해야 하는 단점을 가지고 있다.
래퍼런스 변수는 변수명을 그래로 사용할 수 있다는 점이 편안하다.
또한 래퍼런스는 int* const a = &b;와 같은 기능을 기본으로 제공하고 있다.
int a = 10;
int b = 20;
int & iRef = a;
iRef = b; // a에 b를 대입함.
레퍼런스의 주소값을 한번 할당하고 나면 주소값은 바뀌지 않고 계속 a를 가르킨다. 따라서 다른 변수를 할당하러라도 주소값이 아닌 값을 수정하게 된다.
const키워드를 사용하여 a 자체의 값을 수정할 수 없게 할 수 있다.
int a = 10;
int b = 20;
const int & iRef = a;
iRef = 20; // 오류
정리하자면 래퍼런스 변수는 포인터 변수와 공통점이 많지만 기본적으로 참조변수를 통해서 직접적으로 원본을 수정할 수 없고, 한번 할당된 래퍼런스 변수는 메모리에 해제 되기 전까지 같은 주소를 가지게 된다.
다음 클래스에서의 대입연산자를 살펴보자
class Cmy{
public :
//대입연산자 생략 가능
// 객체 끼리의 대입연산자 사용 시 호출이 됨.
Cmy& operator = (const Cmy& _other) {
// 다른 객체의 레퍼런스 변수를 할당 받아서
// this의 멤버변수에 할당함.
m_i = _other.m_i;
m_f = _other.m_f;
//this포인터를 전달
return *this;
}
}
int main(){
Cmy c1;
Cmy c2;
c2.SetInt(200);
c1 = c2;
return 0;
}
대입연산자는 각각의 래퍼런스 주소를 할당받아서 this의 맴버변수에 _other의 맴버변수의 값을 받고 있다.
main함수에서 c1 = c2부분을 보면 c1객체에 c2를 할당하는데 이는 대입연산자를 호출하게 하여 주소값을 가르키는 것이 아닌 래퍼런스 변수로 다른 래퍼런스의 값에 c1에 해당하는 this 맴버변수에 c2의 맴버변수 값을 할당하는 것으로 연산 이후 c1의 맴버변수들은 c2가 가졌던 맴버변수들의 값을 받을 수 있게 되었다.
대입연산자를 래퍼런스 변수로 정의하게 되어 서로 다른 객체 간 독립성을 유지할 수 있게 되었다.