Class는 객체 지향적 특성이 추가된 개념이다. 객체 지향적 특성의 핵심은 상속과 다형성이다.

상속 대상이 되는 클래스를 기본 클래스 라고 하고, 기본 클래스를 상속받아 새로운 클래스를 정의하면 파생 클래스 라고 한다.

일단 기본 클래스를 Vehicle, 파생 클래스를 Bicycle, Truck 클래스로 해서 정의해보는 연습을 했다.

 

1. Class 구현해보기

#include <iostream>
#include <string>
using namespace std;

//기본 클래스
class Vehicle
{
protected:
	string color;
	int speed;

public:
	Vehicle(string c, int s) : color(c), speed(s) {}

	void move()
	{
		cout << "The Vehicle is moving at " << speed << " km/h" << endl;
	}
	void setColor(string c)
	{
		color = c;
	}

	string getColor()
	{
		return color;
	}

};

//파생 클래스 (Bicycle)
class Bicycle : public Vehicle
{
private:
	bool hasBasket;

public:
	Bicycle(string c, int s, bool basket)
		: Vehicle(c, s), hasBasket(basket) {
	}


		void ringBell()
	{
		cout << "Bicycle bell: Ring RIng!" << endl;
	}
};

//파생 클래스 (Truck)
class Truck : public Vehicle
{
private:
	int cargoCapacity;

public:
	Truck(string c, int s, int capacity)
		: Vehicle(c, s), cargoCapacity(capacity) {
	}
	
	void loadCargo()
	{
		cout << "Truck loading cargo. Capacity: " << cargoCapacity << " kg" << endl;
	}
};

int main()
{
	Bicycle b("Yellow", 30, true);
	Truck t("Blue", 40, 95);

	b.ringBell();
	t.loadCargo();

	return 0;
}

1) Truck(string c, int s, int capacity) : Vehicle(c, s), cargoCapacity(capacity) {} 의 의미 : Truck 생성자 호출 시 코드를 수행하기 전에 Vehicle(c, s), cargoCapacity(capacity)를 실행하라는 의미.

2) Bicycle 클래스는 ringBell 메서드를 통해 벨을 울릴 수 있다.

3) Truck 클래스는 loadCargo 메서드를 통해 짐을 실을 수 있다.

4) Bicycle 클래스는 생성자에서 색상, 속도, basket의 존재 여부를 인자로 받는다.

5) Bicycle 클래스와 Truck 클래스의 생성자는 속도와 색상 값 초기화 시 부모클래스인 Vehicle 클래스의 생성자를 사용한다. 예를 들어, class Truck : public Vehicle 으로 초기화를 한다.

6) Truck 클래스는 생성자에서 색상, 속도, 실을 수 있는 짐의 수용량을 인자로 받는다.

 

2. 다형성

다형성이란,

기본이 되는 클래스를 만들어 함수의 인터페이스를 정의하고,

실제 구현은 파생 클래스에서 담당하게 하는 것이다.

이때 동적 바인딩을 통해 실제 호출된 객체의 타입에 따라 적절한 파생 클래스의 함수가 실행되도록 virtual 을 함수 앞에 붙여야 한다. virtual을 붙이면 가상 함수 테이블이 생성되며 이 테이블에는 기본 클래스에서 virtual로 선언된 함수가 파생 클래스에서 재정의된 경우 그 위치가 기록되어 동적 디스패치가 가능해진다. virtual을 붙이는 경우 가상 함수 테이블을 참조해서 실제 호출한 파생 클래스의 메서드가 호출된다. 

#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
	Animal() {}
	virtual void bark() {}; //virtual 선언. lion.bark , wolf.bark 처럼 안 써도 된다.
};

class Lion : public Animal
{
public:
	Lion(string word) :m_word(word) {}
	void bark()
	{
		cout << "Lion" << " " << m_word << endl;
	}
private:
	string m_word;
};

class Wolf : public Animal
{
public:
	Wolf(string word) :m_word(word) {}
	void bark()
	{
		cout << "Wolf" << " " << m_word << endl;
	}
private:
	string m_word;
};

class Dog : public Animal
{
public:
	Dog(string word) :m_word(word) {}
	void bark()
	{
		cout << "Dog" << " " << m_word << endl;
	}
private:
	string m_word;
};

void print(Animal* animal)
{
	animal->bark();
}

int main()
{
	Lion lion("ahaaaa!");	//변수 메모리 저장
	Wolf wolf("ohhhhh!");
	Dog dog("oooooo!");

	print(&lion);	//변수의 메모리 주소를 가져옴
	print(&wolf);
	print(&dog);


	return 0;
}

virtual를 사용하지 않으면 일일이 animal이 추가될 때마다 메서드를 새로 작성해서 불러와야 한다.

 

3. 순수 가상 함수

순수 가상 함수란, 기본 클래스에서 구현하지 않고, 반드시 파생 클래스에서 구현하도록 강제하는 가상 함수이다. 인터페이스는 필요하지만 기본클래스에서 구현할 필요가 없는 경우 순수 가상 함수로 선언한다. 

순수 가상 함수를 하나 이상 포함하는 클래스를 추상 클래스라고 한다. 추상 클래스는 직접 인스턴스를 생성할 수 없고, 포인터 혹은 참조를 통해서만 사용할 수 있다.

 

사용 예시)

#include <iostream>
using namespace std;

//기본 클래스이자 추상 클래스.
class Animal {
public:
	virtual void makeSound() = 0;
};

//파생클래스가 Dog.
class Dog : public Animal {
public:
	void makeSound() {
    	cout << "Dog barks: Woof! Woof!" << endl;
    }
};

//파생클래스가 Cat.
class Cat : public Animal {
public:
	void makeSound() {
    	cout << "Cat meows: Meow! Meow!" << endl;
    }
};

int main()
{
	Animal* myAnimal;	//Animal타입 포인터.
	Dog myDog;
    Cat myCat;
    
    myAnimal = &myDog;	//Dog 객체 가리키기
    myAnimal->makeSound(); //Dog의 makeSound() 호출

	myAnimal = &myCat;
    myAnimal->makeSound(); //Cat의 makeSound() 호출
    
	return 0;
}

 

 

+ Recent posts