동적 메모리 할당,해제를 맡는 new, delete 키워드

C에서 메모리의 동적할당을 할때 malloc 함수를 사용하지요. 동적할당에 대해 모르신다면 저의 동적할당에 대한 포스팅을 보고 오시기 바랍니다.

reakwon.tistory.com/20

 

[C언어] 동적 메모리 할당의 세가지 방법 malloc, calloc, realloc

동적 메모리 할당 우리는 이제껏 메모리를 할당할때 정적으로 할당했습니다. 어떤 것이냐면 int arr[100]; 이렇게 할당을 했었죠. 뭐 문제없습니다. 실행도 잘 되구요. 하지만 이런 상황은 조금 불

reakwon.tistory.com

 

C언어에서 malloc을 사용할때 반환하는 void*를 형변환하여 사용하였습니다. 그리고 동적할당된 메모리를 해제할땐 free를 사용했습니다. C++에서는 이러한 동적할당에 관한 사용이 자주 이루어지기 때문에 아예 키워드로 지정해놓았습니다. malloc을 대체하는 키워드는 new, free를 대체하는 키워드는 delete입니다. 사용되는 방식은 다음과 같습니다.

MyClass* myclass = new MyClass();
delete(myclass);

여기서 malloc과 new는 동적할당하는 것은 같지만 malloc은 함수를 call하는 것이고 new는 C++에서 기본적으로 제공하는 키워드이기 때문에 별도의 라이브러리를 추가할 필요가 없습니다. 또한 new는 생성자를 자동호출하는 특징이 있지요. 이제 C++에서는 객체를 할당할때 malloc으로 하지 않고 new를 이용하여 할당합니다.

오버라이딩(Overriding)

Class의 상속에 대해서 배우셨나요? 그렇다면 이제 다형성을 배워볼 차례입니다. 다형성이라하면 여러 형태를 갖는 객체의 특징을 말하는데요. 아래의 코드를 가지고 다형성과 관련한 클래스의 특징들을 천천히 살펴보도록 합시다.

#include<iostream>
using namespace std;

class Animal {
private:
	int height;
	int weight;
public:
	Animal(int _height, int _weight) :height(_height), weight(_weight) {}
	void printInfo() {
		cout << "==============정보=============" << endl; ;
		cout << "키:" << height << "무게:" << weight << endl;
	}
	int getHeight() {
		return height;
	}
	int getWeight() {
		return weight;
	}
};

class Human :public Animal {
private:
	int race;
public:
	Human(int _height, int _weight, int _race) :Animal(_height, _weight) {
		race = _race;
	}
	void printInfo() {
		cout << "==============정보=============" << endl;
		cout << "키:" << getHeight() << "무게:" << getWeight() << endl;
		cout << "인종:";
		if (race == 0)
			cout << "황인" << endl;
		else if (race == 1)
			cout << "흑인" << endl;
		else if (race == 2)
			cout << "백인" << endl;
		else
			cout << "혼혈" << endl;
	}
};
int main() {
	
	Animal* animal = new Animal(50, 20);
	animal->printInfo();

	Human* human = new Human(150, 80, 3);
	human->printInfo();
	delete(animal);
	delete(human);
}

 

여기서 Animal클래스는 Human클래스의 부모 클래스입니다. main에서는 둘 다 new 키워드로 생성해서 정보를 출력하는 코드네요. Human 클래스에서 printInfo 메소드를 보면 cout 두줄이 정확히 Animal의 printInfo클래스의 메소드와 같은 것을 알 수 있습니다. 

어차피 Human클래스는 Animal클래스를 상속받고 있으니, Animal클래스의 printInfo를 재활용 할 순 없을까요? 그럴때 부모클래스::메소드명 을 호출하여 부모클래스의 메소드를 사용할 수 있습니다. Human 클래스의 메소드를 아래처럼 바꿔봅시다.

void printInfo() {
	Animal::printInfo();
	cout << "인종:";
	if (race == 0)
		cout << "황인" << endl;
	else if (race == 1)
		cout << "흑인" << endl;
	else if (race == 2)
		cout << "백인" << endl;
	else
		cout << "혼혈" << endl;
}

 

이처럼 부모클래스의 함수를 이용하여 자식 클래스에서 같은 메소드 이름으로 새로운 기능을 덧붙이는 방식을 오버라이딩이라고 합니다.

 

 

다형성(Polymorphism)

OOP(Object-Oriented Programming)은 현실 세계를 반영한다는 슬로건을 가지고 있습니다. 위의 코드를 현실세계와 연관지어 생각해봅시다. 우리 사람은 동물의 한 종이죠. 그래서 동물의 속성을 갖고 있습니다. 하지만 동물은 사람이 아니죠. 동물에는 사람외에도 개와 고양이 등 많이 있기 때문인데요. 정리하면 이렇게 되겠네요.

사람은 동물이다.(O)

동물은 사람이다.(X)

위에서 성립이 되는 상황(사람은 동물)을 C++에서는 아래와 같이 표현할 수 있습니다. main함수안의 내용을 아래와 같이 바꿔서 실행해보시기 바랍니다.

Animal* human = new Human(150, 80, 3);
human->printInfo();

 

이렇게 부모클래스를 통해 만들어진 객체가 자신을 상속받는 여러 클래스의 객체로 모양을 띄는 것을 다형성이라고 합니다. OOP에서 매우 중요한 개념입니다.

위의 코드를 수행한 결과에서 한가지 불편한 점이 있는데요. 저희는 human->printInfo()가 당연히 Human클래스의 printInfo를 출력한다는 믿음으로 프로그래밍을 했는데, 막상 결과는 Animal의 printInfo가 호출됩니다. 어떻게 해야지 우리가 원하는 동작을 할 수 있을까요?

virtual 키워드

만약 자신을 상속받는 자식 클래스의 객체가 자신의 메소드를 사용하는데, 이것을 오버라이딩했다면 자식 클래스의 객체 메소드를 호출하라고 지정하는 방법은 메소드 앞에 virtual 키워드를 사용하는 것입니다. 자신의 메소드는 가상으로 만들어져 있으니 자식의 메소드를 호출하라는 의미가 되겠죠. 그래서 우리의 목표를 달성하기 위해서는 Animal 클래스의 printInfo메소드 앞에 virtual 키워드를 추가하면 됩니다. 그렇게 되면 Human의 printInfo 메소드를 호출하게 됩니다.

class Animal {
private:
	int height;
	int weight;
public:
	Animal(int _height, int _weight) :height(_height), weight(_weight) {}
	virtual void printInfo() {
		cout << "==============정보=============" << endl; ;
		cout << "키:" << height << "무게:" << weight << endl;
	}
	int getHeight() {
		return height;
	}
	int getWeight() {
		return weight;
	}
};

 

virual키워드를 사용해야 자식 객체의 메소드를 사용한다고 했죠? 그렇다면 다음의 상황을 예측해봅시다.

class Human :public Animal {
private:
	int race;
public:
	//...생략...//
	void printInfo() {
		//...생략...//
	}
};

class Student :public Human {
private:
	char grade;
public:
	Student(int _height, int _weight, int _race,char _grade) :Human(_height, _weight, _race) {
		grade = _grade;
	}

	void printInfo() {
		Human::printInfo();
		cout << "성적:" << grade << endl;
	}
};

 

Human 클래스는 printInfo에 virtual키워드를 넣지 않았습니다. Student는 Human클래스를 상속받고 있는데, 이때 printInfo는 Human의 printInfo를 호출할까요? 메소드의 virtual이 지정되면 이후 자식은 자동으로 virtual 키워드가 적용이 됩니다. 그래서 결론은 Student의 printInfo가 호출이 되지요. 하지만 명시적으로 virtual을 지정해주는 것이 관례입니다. 코드의 가독성과 이해를 돕기 위해서지요.

자, 이런 다형성은 언제 필요할까요? 예를 들면 함수에서 해당 클래스를 상속하는 모든 객체를 받을 때가 그 예가 됩니다. 아래의 함수에서 변수로 animal를 가리키는 포인터를 받습니다. 이 함수의 목적은 animal인 객체의 printInfo를 출력하는 것인데, Animal클래스를 상속하는 모든 객체를 받을 수 있습니다. 

void printInfo(Animal *animal) {
	animal->printInfo();
}

 

 

this 포인터

객체가 생성될때 자기 자신을 가리키는 포인터가 this 포인터라고 합니다. 만일 아래와 같은 상황에서 this를 사용할 수 있습니다. 저는 생성자에서 객체의 grade를 전달받은 grade의 값으로 입력하고 싶어서 아래와 같은 코드를 사용했습니다.

class Student :public Human {
private:
	char grade;
	Student(int _height, int _weight, int _race,char grade) :Human(_height, _weight, _race) {
		grade = grade;
	}
//...생략...//
};

 

여기서 객체의 grade는 절대 변할 수 없습니다. 객체의 grade보다 매개 변수인 grade가 우선시 되기 때문에 매개변수 grade에 다시 매개변수의 grade의 값을 넣기 때문입니다. 이럴때 사용할 수 있는 것이 바로 this포인터를 사용하는 것이죠.

Student(int _height, int _weight, int _race,char grade) :Human(_height, _weight, _race) {
	this->grade = grade;
}

this포인터는 객체가 생성되고 난 이후에 그 효력을 발휘합니다. 그 객체의 주소를 나타내야하기 때문이죠. 아래와 같이 한번 주소를 찍어봅시다. 정확히 같은 곳을 가리키는 것을 알 수 있습니다.

class Student :public Human {
private:
	char grade;
public:
	Student(int _height, int _weight, int _race,char grade) :Human(_height, _weight, _race) {
		cout <<"this 포인터의 주소"<< this << endl;
		this->grade = grade;
	}
	//...생략...//
};

int main() {
	
	Animal* student = new Student(165, 55, 0, 'A');
	cout <<"student의 주소"<< student << endl;
	student->printInfo();
	delete(student);
}

 

이상 다형성과 관련된 포스팅을 마치도록 하겠습니다. 이해가 되지 않는 부분은 댓글로 남겨주세요.

 

 

반응형
블로그 이미지

REAKWON

와나진짜

,