Cpp - 생성자, 소멸자, 참조변수, 오버로딩

eelijus·2022년 5월 4일
0

Cpp/C++

목록 보기
1/10

C02

💡 생성자, 소멸자, 생성자 오버로딩, 초기화 리스트, 함수 오버로딩, 연산자 오버로딩, const

생성자(Constructor)

  • 생성자를 사용하는 이유

    클래스를 정의한 후 클래스 객체를 생성하게 되면 메모리에 할당된다. 이때 클래스의 멤버 변수는 초기화되지 않은 상태이므로 사용할 수 없다. 일일이 멤버 변수에 접근하여 초기화 후 사용해도 되지만 만약 private처럼 외부 접근이 불가능한 상태라면 불가능하고 public설정이 된 멤버 함수를 이용하여 초기화해야 할 것이다. 이럴 경우에는 객체를 사용하기 전에 무조건 해당 함수를 실행하고 객체를 사용해야 한다. 따라서 C++에서는 객체의 생성과 동시에 멤버 변수를 초기화해주는 멤버 함수인 생성자(constructor)를 제공한다.

  • 클래스의 객체 생성 시(인스턴스 생성 시)에 private 멤버를 초기화

  • 생성자 이름 == 클래스 이름

  • 객체 생성 시 딱 한 번 호출됨

  • 디폴트값 설정 가능

복사 생성자(Copy Constructor)

: 동일한 클래스 타입의 다른 객체에 대한 참조(reference)를 인자로 받아 생성하는 객체를 초기화하도록 하는 깊은 복사(객체가 가진 멤버의 값과 형식 자체를 복사해 객체 자체를 복사).

class TestClass {
	private:
			int num1;
			int num2;
	public:
			//기본 생성자
			TestClass(int a, int b) {
				num1 = a;
				num2 = b;
			}
			//복사 생성자
			TestClass(TestClass& original) {
				num1 = original.num1;
				num2 = original.num2;
			}
			void	printNumber() const {
				std::cout << "num1 : " << num1 << std::endl;
				std::cout << "num2 : " << num2 << std::endl;
 			}	
};

int main() {
	//기본 생성자로 객체 생성
	TestClass test1(12, 06);
	//복사 생성자로 객체 생성
	TestClass test2(test1);
	
	test2.printNumber();

}

멤버 이니셜라이저(Member Initializer)

멤버 변수를 초기화하는 방법 중 하나(다른 하나는 생성자를 통한 멤버 변수 초기화)

const 멤버의 경우 생성과 동시에 초기화되어야하기 때문에 const 멤버 변수는 이니셜라이저를 통해 초기화해야함.

TestClass():num1(12), num2(06) { //추가 초기화 }

참조 변수(레퍼런스 / reference variable)

💡 변수의 종류

일반 변수값을 저장하기 위해 메모리에 공간을 할당받아 직접 저장하는 함수
포인터 변수다른 변수의 주소값을 저장하는 변수
참조 변수다른 객체나 다른 객체에 대한 별명

: 크키가 큰 객체를 함수에 인자로 전달할 때 주로 사용. 객체간의 연결고리 ?

//자료타입& 참조변수명 = 변수명
int  original = 1206
int& refer = original 

여기서의 &연산자는 주소연산자와는 다르다.

  • 참조 변수는 저장하는 변수와 같은 메모리를 참조한다

  • 참조 변수는 선언과 동시에 초기화돼야함

  • null값 참조 불가

  • const는 const로, non-const는 non-const로 참조

  • 한번 참조한 변수는 재참조할 수 없다.

  • 함수의 매개변수로의 사용

void swapValue(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

int	main() {
	int x = 10;
	int y = 20;

	std::cout << "x: " << x << " y: " << y << std::endl;
	swapValue(x, y);
	std::cout << "x: " << x << " y: " << y << std::endl;
}

기존이라면 swapValue의 매개변수를 포인터로 받아 x, y의 주소값을 넘겨줬겠지.

인자에 x, y가 전달되면 매개변수 a, b가 x, y에 대한 참조로 선언된다.

  • 포인터와 참조 변수
int main() {
	int num = 1206;
	//num의 주소값을 ptr에 저장
	int *ptr = &num;
	//참조변수 선언 후 num 저장
	int& ref = num;

	std::cout << *ptr << std::endl;
	std::cout << ref << std::endl;
}
//OUTPUT
1206
1206

따라서 *ptr과 ref는 동일하게 취급됨.

참조 변수는 선언과 동시에 초기화돼야 하고, null로 초기화하거나 참조를 변경할 수 없으므로 포인터보다 안전하게 취급받는다.

this pointer

  • this pointer를 사용하는 이유

모든 멤버 함수는 자신만의 this 포인터를 가지고 있음.

this pointer : 멤버 함수를 호출한 객체 자신을 가리키는 포인터(주소값을 갖고 있음. 포인터니까)

이를 사용해 호출된 멤버 함수는 자신을 호출한 객체가 어떤 객체인지 알 수 있다.

생성자 오버로딩

생성자도 일종의 함수라 오버로딩 가능

소멸자(Destructor)

주로 클래스에 동적할당을 통해 메모리를 할당받는 멤버 변수가 존재할 경우 메모리 할당해제를 위해 사용됨

💡 **각 영역별로 메모리를 할당받고 사용 종료 후, 할당받은 메모리가 해제되는 시기**
스택 영역해당 스코프 ( {} ) 가 끝날 때
힙 영역delete 명령을 통해 메모리를 해제할 때
데이터 영역프로그램이 종료될 때 (전역, 정적 변수 등 프로그램 전체에서 사용되므로)

따라서 힙 영역에 메모리를 할당받은 동적 할당의 경우 소멸자를 통해 메모리를 해제해줘야 함

함수 오버로딩(function overloading)

: 매개변수의 개수나 형태를 다르게 설정하여 동일한 이름의 함수를 여러개 만드는 것

  • 함수 오버로딩을 쓰는 이유

    인자 2개를 받아 두 수의 합을 구하는 함수를 구현한다고 하자.

    자연수, 정수, 실수 등 다양한 수의 합을 구하려면 그때마다 아래와 같이 새로운 이름의 새로운 함수를 생성해야 한다.

    void	printIntAdd(int x, int y) {
    	std::cout << "합 : " << x + y << std::endl;
    }
    
    void	printDouubleAdd(double x, double y) {
    	std::cout << "합 : " << x + y << std::endl;
    }
    
    int main() {
    
    	printIntAdd(12, 6);
    	printDouubleAdd(12.06, 19.94);
    
    	return (0);
    
    }

    이 경우 함수 오버로딩을 사용해 다양한 자료형에 대해 동일한 기능을 수행하는 함수를 생성해 불필요한 코드의 복잡성을 방지할 수 있다.

    void	printAdd(int x, int y) {
    	std::cout << "합 : " << x + y << std::endl;
    }
    
    void	printAdd(double x, double y) {
    	std::cout << "합 : " << x + y << std::endl;
    }
    
    int main() {
    
    	printAdd(12, 6);
    	printAdd(12.06, 19.94);
    
    	return (0);
    
    }

연산자 오버로딩

말그대로 연산자 오버로딩

  • 예시 : 연산자 오버로딩을 사용한 객체끼리 + 시켜보자
//난 Point(좌표) class를 만들어서, Point 객체끼리의 합(좌표 덧셈)을 하고싶어

class Point {
	private :
			int x;
			int y;
	public :
			Point(int a = 0, int b = 0) : x(a), y(b) {};
			
			void print() {
				std::cout << "x좌표: " << x << " y좌표: " <<<< std::endl;
			}

			//매개변수로 참조 변수를 받으니까 값을 조작할 수 있겠지?
			Point operator+(Point& p) {
				Point point;
				//연산자도 매개변수를 받는 함수같은 것임. 즉 p1 + p2면 p1이 +를 호출해 인자로 p2를 넘길거다.
				point.x = this->x + p.x;
				point.y = this->y + p.y;

				return point;
			}
};

int	main() {
	
	//객체(인스턴스) 생성과 초기화
	Point p1(1, 3), p2(2, 7);

	Point result = p1 + p2;
	result.print();

	return (0);
}

대입 연산자 (assignation operator)

: 객체의 멤버를 모두 복사해주는 연산자

profile
sujileelea

0개의 댓글