[C++ 정리] 상속의 기본형과 형변환 2015-06-23

부모 클래스에서 미리 구현된 내용을 상속받아 자식클래스에서 확장한다. 또한 부모 자식관계의 클래스에서 형변환은 기본적으로 ‘자식에 부모를 담지못한다’, ‘부모에 자식은 담을 수 있다’를 유념한다.

class MyClassParent
{
public:
	MyClassParent();
	MyClassParent(int x, int y)
	{
		this->width = x;
		this->height = y;
	}

protected:
	int width;
	int height;

private:
	// Error - private로 선언시 상속받은 클래스에서 접근할 수 없다.
	//  => protected 에 선언한다.
	//int width;
	//int height;
	
};

// 다음과 같은 형태로 상속 받는다.
class MyClass :public MyClassParent
{
public:
	MyClass();
	int getArea()
	{
		return this->width * this->height;
	}
	int initArea;
};

// 다음과 같은 형태로 부모클래스의 생성자를 호출할 수 있다.
MyClass::MyClass():MyClassParent(100, 20)
{
	this->initArea = this->width * this->height;
}

// - END header

int _tmain(int argc, _TCHAR* argv[])
{
	// 상속의 예
	MyClass initClass;
	cout << initClass.initArea;

	// 1>> 형변환	
	// [Error] 자식클래스에 부모객체는 담지 못한다.
	MyClassParent parentClass1;
	/*MyClass childClass1 = parentClass1;*/
	// [OK] 부모클래스에 자식객체는 담을 수 있다.
	MyClass childClass2;
	MyClassParent parentClass2 = childClass2;
	
	// 2>> 포인터간 형변환
	// [ERROR] 자식클래스의 포인터에 부모객체의 주소는 담지 못한다.
	MyClassParent parentClassPointer1;	
	/*MyClass* childClassPointer1 = &parentClassPointer1;*/
	// [OK] 부모클래스의 포인터에 자식객체의 주소는 담을 수 있다.
	MyClass childClassPointer2;	
	MyClassParent* parentClassPointer2 = &childClassPointer2;
	
	// 레퍼런스간 형변환
	// [ERROR] 자식클래스의 참조값에 부모객체의 주소는 담지 못한다.
	MyClassParent parentClassReference1;
	/*MyClass childClassReference1 = &parentClassReference1;*/
	// [OK] 부모클래스의 참조값에 자식객체의 주소는 담을 수 있다.
	MyClass childClassReference2;
	MyClassParent& parentClassReference1 = childClassReference2;

	return 0;
}
* 자식클래스에서는 부모클래스의 public: & protected: 에만 접근이 가능하다.
* 부모클래스에 자식객체는 담을 수 있다. 반대로는 불가하다.
* 부모클래스의 포인터에 자식객체의 주소는 담을 수 있다. 반대로는 불가하다.
* 부모클래스의 참조값에 자식객체의 주소는 담을 수 있다. 반대로는 불가하다.

[C++ 정리] 클래스 기본형태 및 인라인, const 함수의 사용 2015-06-22

객체지향 프로그래밍을 위해 다음과 같이 클래스를 선언하고 사용한다. 일반적으로 클래스의 선언은 *.h파일에 구현은 *.cpp에 포함 시킨다.

#include "stdafx.h"
#include <iostream>

using namespace std;

// 일반적으로 *.h 파일에 정의
class MyClass
{	
public:
	MyClass();
	// 생성자 오버로딩 (EX1)
	MyClass(int x, int y);
	int getArea();
	int getArea(bool isConst) const;
	// 외부에 선언한 인라인 함수
	void MyClass::inlineFunction();

protected:

private:
	int width;
	int height;
	int area;

};

// - END header

// 일반적으로 *.cpp 파일에 정의
MyClass::MyClass()
{
	width = 0;
	height = 0;
	area = 0;
}

// 생성자로 멤버를 초기화하는 방법 (EX2)
MyClass::MyClass(int x, int y):width(x), height(y), area(x*y){}

// - END cpp

// 넓이를 얻어오는 방법
int MyClass::getArea()
{
	return area;
}

int MyClass::getArea(bool isConst) const
{
	/*
	// - const 함수에서 멤버변수 수정은 불가능하다. (읽기만 가능하다)
	if (isConst)
		area = area + 100;
	*/
	return area;
}

// 외부에 선언한 인라인 함수 (EX3)
inline void MyClass::inlineFunction() 
{
	cout << "인라인함수 테스트" "\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
	// 인자없는 생성자
	MyClass myClass1;
	cout << myClass1.getArea() << "\n";

	// 오버로딩된 생성자
	MyClass myClass2(10,20);
	cout << myClass2.getArea() << "\n";

	// 인라인 함수의 실행
	myClass1.inlineFunction();

	// const 멤버함수 테스트 : const 멤버함수에서는 멤버변수의 값을 변경할 수 없다. (EX4)
	MyClass myClass3(20, 30);
	cout << myClass3.getArea(true) << "\n";

	// const 객체 : const 객체는 const 멤버함수만 호출할 수 있다.
	const MyClass myClass4(30, 40);
	cout << myClass4.getArea(true) << "\n";
	// [ERROR] - 아래는 일반함수를 접근하였기 때문에 에러가 발생한다.
	// cout << myClass4.getArea() << "\n";
	
	return 0;
}
* (EX1) 생성자 또한 오버로딩하여 사용할 수 있다.
* (EX2) ":width(x), height(y), area(x*y)" 와 같이 생성자 구현 부에서 멤버변수를 초기화 할 수 있다.
* (EX3) 인라인 함수를 사용하면 실제로는 main에 함수 내용이 구현되어 있는것과 같이 동작해 성능향상을 가져올 수 있으나 메모리에 로딩하는 것이 부담이 될 정도로 코드가 길다면 역효과를 낼 수 있다.
* (EX4) const 로 선언된 멤버함수에서는 멤버변수의 값을 변경할 수 없다.
* (EX5) const 로 선언된 객체는 const 멤버함수만 호출 가능하다.

[C++ 정리] 객체지향 프로그래밍 및 관련 개념 2015-06-22

객체 지향 프로그래밍(Object-Oriented Programming, OOP) : 프로그래밍 패러다임의 하나로 프로그램을 객체들의 모임으로 보고 개발하는 것이다. 유연하고 변경이 용이하여 대규모 소프트웨어 개발에 많이 사용된다.

장점

* 유연하고 변경이 용이하다. (프로그램의 구조를 파악하기 쉬워 개발과 보수가 간편)

단점

* 지나치게 객체화 하는 것은 실제의 개념과 너무 달라서 이해하기가 쉽지 않아질 수 있다.

특성

* 크게 다음 네가지의 동작 과정이 있다.
  - 클래스(class) : 추상 자료형(빵틀)
  - 객체(object) : 추상 자료형의 인스턴스(빵틀을 통해 만들어진 빵)
  - 메소드(method) : 추상 자료형에 정의된 연산(빵을 자르는 명령)
  - 메시지(message) : 메소드의 호출(빵을 자르는 명령을 실행)
* 클래스는 정보은닉(Data Hiding)을 위해 캡슐화(Encapsulation)를 지향해야 한다.
  : 내부 구현내용은 보이지 않도록 하고 약속된 부분만을 보이도록 지정한다.
  => 새로운 클래스에서 약속되지 않은부분을 추측해서 개발 했을 경우 문제가 발생할 수 있다.
* 기존 클래스의 기능을 상속(Inheritance)받는다.
  : 하위 클래서에서는 기존 기능을 응용해 추가적인 요구사항을 개발할 수 있다.
  => 클래스간의 종속관계를 형성시켜 객체를 조직화할 수 있고 소스코드의 재사용성을 더해준다.
* 2개 이상의 클래스로부터 상속받을 수 있다.(다중상속)
  => 클래스의 상속관계에 혼란을 줄 수 있으므로 주의해야한다.
* 다형성(polymorphism)을 위해 다음과 같은 개념을 사용한다.
  : 객체의 메소드를 여러가지 개념으로 사용 되도록 할 수 있다.
  => 클래스의 상속관계에서 가상함수(Virtual Functions)를 사용
  => 메소드를 오버라이딩(Overriding - 같은 이름의 메소드가 여러 클래스에서 다른기능을 하는것)
     ※ 오버라이딩을 위해 순수가상함수(Pure Virtual Functions)를 정의한다.
     ※ 순수가상함수가 1개 이상 선언된 클래스를 추상클래스(Abstract Class)라 부른다.
  => 메소드를 오버로딩(Overloading - 같은 이름의 메소드가 인자의 갯수나 자료형에 따라서 다른 기능을 하는것)
#include "stdafx.h"
#include <iostream>

using namespace std;

// Ex1> 클래스(Class)의 선언
class Dot
{
public:
	Dot();
	// Ex2> 생성자 Dot을 오버로딩(Overloading) 한다.
	Dot(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
	int x;
	int y;
	// Ex3> 가상함수(Virtual Functions)를 선언한다.
	virtual int Cal()
	{
		return this->x - y;
	}
	// Ex4> 일반 멤버함수를 선언한다.
	void NotVirtual()
	{
		cout << "DOT CALL\n";
	}
	// Ex5> 순수가상함수(Pure Virtual Functions)를 선언한다.
	virtual void PureVirtual() const = 0;
};

// Ex6> Dot 클래스를 상속(Inheritance) 받는다.
class DotA:public Dot
{
public:
	DotA();
	DotA(int x, int y);
	// virtual는 생략가능하다.
	virtual int Cal()
	{
		return this->x + this->y;
	}
	void NotVirtual()
	{
		cout << "DOTA CALL\n";
	}
	virtual void PureVirtual() const
	{
		cout << "DOTA CALL\n";
	}
};
DotA::DotA(int x, int y):Dot(x, y)
{
	cout << "SET DotA\n";
}

class DotB :public Dot
{
public:
	DotB();
	DotB(int x, int y);
	// virtual는 생략가능하다.
	virtual int Cal()
	{
		return this->x * this->y;
	}
	void NotVirtual()
	{
		cout << "DOTB CALL\n";
	}
	virtual void PureVirtual() const
	{
		cout << "DOTB CALL\n";
	}
};
DotB::DotB(int x, int y) :Dot(x, y)
{
	cout << "SET DotB\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
	// 가상함수의 사용
	Dot* dot[2] = {};
	// Ex7> 객체 생성
	dot[0] = new DotA(100, 20);
	dot[1] = new DotB(100, 20);

	cout << "\n######## Virtual Functions ########\n";
	// 가상함수의 사용
	cout << "DotA Result : " << dot[0]->Cal() << "\n";
	cout << "DotB Result : " << dot[1]->Cal() << "\n";

	cout << "\n######## Member Functions ########\n";
	// 일반멤버함수의 사용
	dot[0]->NotVirtual();
	dot[1]->NotVirtual();

	cout << "\n######## Pure Virtual Functions ########\n";
	// 순수가상함수의 사용
	dot[0]->PureVirtual();
	dot[1]->PureVirtual();

	// Ex8> 추상클래스(Abstract Class) 생성
	// 순수가상함수"virtual void PureVirtual() const = 0;"를 선언하므로써 Dot은 추상클래스(Abstract Class)가 되었다.
	// 즉, 추상클래스는 말그대로 추상화된 클래스(구체화 되지않은 클래스)이기 때문에 상속받아 구현을 해야만 사용할 수 있다.
	// Error - 추상클래스를 상속받은 클래스를 생성하지 않고 그냥 생성하였다.
	// Dot dotTest = new Dot(100, 20);

	return 0;
}
Ex1> 클래스(Class) 선언
Ex2> 오버로딩(Overloading)
Ex3> 가상함수(Virtual Functions) 선언
Ex4> 멤버함수 선언
Ex5> 순수가상함수(Pure Virtual Functions) 선언
Ex6> 클래스 상속(Inheritance)
Ex7> 객체(object) 생성
Ex8> 추상클래스(Abstract Class) 생성

object