|
|
[9장 연습문제]
// 1
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Converter {
protected:
double ratio;
virtual double convert(double src) = 0;
virtual std::string getsource() = 0;
virtual std::string getdest() = 0;
public:
Converter(double ratio);
void run();
};
class Wtd : public Converter {
public:
Wtd(double ratio);
protected:
double convert(double src) override;
std::string getsource() override;
std::string getdest() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Wtd wtd(1010);
wtd.run();
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Converter::Converter(double ratio) : ratio(ratio) {}
void Converter::run() {
double src;
cout << getsource() << "을 " << getdest() << "로 바꿉니다. " << std::endl;
cout << getsource() << "을 입력하세요>> ";
cin >> src;
cout << "변환 결과: " << convert(src) << " " << getdest();
}
Wtd::Wtd(double ratio) : Converter(ratio) {}
double Wtd::convert(double src) { return src / ratio; }
string Wtd::getsource() { return "원"; }
string Wtd::getdest() { return "달러"; }
}
// 2
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class KTM : public Converter {
public:
KTM(double ratio);
protected:
double convert(double src) override;
std::string getsource() override;
std::string getdest() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
KTM ktm(1.109344);
ktm.run();
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
KTM::KTM(double ratio) : Converter(ratio) {}
double KTM::convert(double src) { return src * ratio; }
string KTM::getsource() { return "Km"; }
string KTM::getdest() { return "마일"; }
}
// 3
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Loopadder {
std::string name;
int x, y, sum;
void read();
void write();
protected:
Loopadder(std::string name="");
int getx();
int gety();
virtual int cal() = 0;
public:
void run();
};
class Forloopadder : public Loopadder {
public:
Forloopadder(std::string name);
protected:
int cal() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Forloopadder x("For Loop");
x.run();
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Loopadder::Loopadder(string name) : name(name), x(0), y(0), sum(0) {}
void Loopadder::read() {
cout << name << " 시작합니다. 두 수를 입력하세요>> ";
cin >> x >> y;
}
void Loopadder::write() {
cout << x << "에서 " << y << "까지의 합은 " << sum << "입니다." << std::endl;
}
int Loopadder::getx() { return x; }
int Loopadder::gety() { return y; }
void Loopadder::run() {
read(); sum = cal(); write(); }
Forloopadder::Forloopadder(string name) : Loopadder(name) {}
int Forloopadder::cal() {
int s = 0;
for (int i = getx(); i <= gety(); ++i) s += i;
return s; }
}
// 4
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Whileloop : public Loopadder {
public:
Whileloop(std::string name);
protected:
int cal() override;
};
class Dowhileloop : public Loopadder {
public:
Dowhileloop(std::string name);
protected:
int cal() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Whileloop whileloop("While Loop");
Dowhileloop dowhileloop("Do While Loop");
whileloop.run();
dowhileloop.run();
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Whileloop::Whileloop(string name) : Loopadder(name) {}
int Whileloop::cal() {
int s = 0, i = getx();
while (i <= gety()) { s += i++; }
return s; }
Dowhileloop::Dowhileloop(string name) : Loopadder(name) {}
int Dowhileloop::cal() {
int s = 0, i = getx();
do { s += i++; } while (i <= gety());
return s; }
}
// 5
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Gate {
protected:
bool x, y;
public:
void set(bool x, bool y);
virtual bool operation()=0;
};
class Andgate : public Gate {
public:
bool operation() override;
};
class Orgate : public Gate {
public:
bool operation() override;
};
class Xorgate : public Gate {
public:
bool operation() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Andgate andgate;
Orgate orgate;
Xorgate xorgate;
andgate.set(true, false);
orgate.set(true, false);
xorgate.set(true, false);
cout.setf(ios::boolalpha);
cout << andgate.operation() << endl;
cout << orgate.operation() << endl;
cout << xorgate.operation() << endl;
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
void Gate::set(bool x, bool y) { this->x = x; this->y = y; }
bool Andgate::operation() { return x && y; }
bool Orgate::operation() { return x || y; }
bool Xorgate::operation() { return x != y; }
}
// 6
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Abstack {
public:
virtual bool push(int n) = 0;
virtual bool pop(int& n) = 0;
virtual int size() = 0;
};
class Intstack : public Abstack {
int* p;
int capacity;
int top;
public:
Intstack(int capacity);
~Intstack();
bool push(int n) override;
bool pop(int& n) override;
int size() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Intstack stack(10);
stack.push(10);
int n;
stack.pop(n);
cout << n << endl;
cout << "현재 스택에 저장된 정수의 개수>> " << stack.size() << endl;
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Intstack::Intstack(int capacity) : capacity(capacity), top(-1) {
p = new int[capacity]; }
bool Intstack::push(int n) {
if (top >= capacity - 1) return false;
p[++top] = n; return true; }
bool Intstack::pop(int& n) {
if (top < 0) return false;
n = p[top--]; return true; }
int Intstack::size() { return top + 1; }
Intstack::~Intstack() { delete[] p; }
}
// 7
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Shape {
protected:
std::string name;
int w, h;
public:
Shape(std::string name="", int w=0, int h=0);
virtual double getarea() = 0;
std::string getname();
};
class Oval : public Shape {
public:
Oval(std::string n, int w, int h) : Shape(n, w, h) {}
double getarea() override;
};
class Rect : public Shape {
public:
Rect(std::string n, int w, int h) : Shape(n, w, h) {}
double getarea() override;
};
class Tri : public Shape {
public:
Tri(std::string n, int w, int h) : Shape(n, w, h) {}
double getarea() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
Shape *p[3];
p[0] = new Oval("빈대떡", 10, 20);
p[1] = new Rect("잘떡", 30, 40);
p[2] = new Tri("토스트", 30, 40);
for(int i = 0; i < 3; i++) {
cout << p[i]->getname() << "넓이는 " << p[i]->getarea() << endl;
delete p[i];
}
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Shape::Shape(string name, int w, int h) : name(name), w(w), h(h) {}
string Shape::getname() { return name; }
double Oval::getarea() { return (w / 2.0) * (h / 2.0) * 3.14; }
double Rect::getarea() { return (double)w * h; }
double Tri::getarea() { return (w * h) / 2.0; }
}
// 8
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Printer {
protected:
std::string model, manufac;
int printedcnt = 0, leastpaper;
public:
Printer(std::string model, std::string manufac, int leastpaper);
virtual ~Printer() {}
virtual void print(int papers) = 0;
virtual void show() = 0;
};
class InkjetPrinter : public Printer {
int Ink;
public:
InkjetPrinter(std::string model, std::string manufac, int leastpaper, int Ink);
void print(int papers) override;
void show() override;
};
class LaserPrinter : public Printer {
int toner;
public:
LaserPrinter(std::string model, std::string manufac, int leastpaper, int toner);
void print(int papers) override;
void show() override;
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
InkjetPrinter ink("kdjskj", "dskn", 5, 10);
LaserPrinter laser("jdns", "wdu", 3, 20);
cout << "잉크젯 : "; ink.show();
cout << "레이저 : "; laser.show(); cout << endl;
while (1) {
int model, n;
cout << "프린터(1:잉크젯, 2:레이저)와 매수 입력>>";
cin >> model >> n;
if (model == 1) ink.print(n);
else if (model == 2) laser.print(n);
ink.show();
laser.show();
cout << "계속 프린트하시겠습니까(y/n)>>";
char x; cin >> x;
if (x == 'n') break;
}
return 0;
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
Printer::Printer(string model, string manufac, int leastpaper)
: model(model), manufac(manufac), leastpaper(leastpaper) {}
InkjetPrinter::InkjetPrinter(string model, string manufac, int leastpaper, int Ink)
: Printer(model, manufac, leastpaper), Ink(Ink) {}
void InkjetPrinter::print(int papers) {
if (leastpaper < papers) { cout << "용지가 부족하여 출력할 수 없습니다." << endl; }
else if (Ink < papers) { cout << "잉크가 부족하여 출력할 수 없습니다." << endl; }
else { leastpaper -= papers; Ink -= papers; cout << "프린트하였습니다." << endl; } }
void InkjetPrinter::show() {
cout << model << " ," << manufac << " ,남은 종이 ";
cout << leastpaper << "장 ,남은 잉크 " << Ink << endl; }
LaserPrinter::LaserPrinter(string model, string manufac, int leastpaper, int toner)
: Printer(model, manufac, leastpaper), toner(toner) {}
void LaserPrinter::print(int papers) {
if (leastpaper < papers) { cout << "용지가 부족하여 출력할 수 없습니다." << endl; }
else if (toner < papers) { cout << "토너가 부족하여 출력할 수 없습니다." << endl; }
else { leastpaper -= papers; toner -= papers; cout << "프린트하였습니다." << endl; } }
void LaserPrinter::show() {
cout << model << " ," << manufac << " ,남은 종이 " << leastpaper << "장";
cout << " ,남은 토너 " << toner << endl; }
}
// 9
// 9.h
#ifndef HEADER_9_H
#define HEADER_9_H
#include <string>
namespace header_9 {
class Shape {
protected:
Shape* next;
public:
Shape() { next = nullptr; }
virtual ~Shape() {}
void add(Shape* p);
Shape* getNext() { return next; }
virtual void draw() = 0;
};
class Line : public Shape {
public:
void draw() override;
};
class Rect : public Shape {
public:
void draw() override;
};
class Circle : public Shape {
public:
void draw() override;
};
class UI {
public:
static int menu();
static int shapeType();
static int deleteIndex();
};
class GraphicEditor {
Shape *pStart, *pLast;
public:
GraphicEditor() { pStart = pLast = nullptr; }
void insert(int type);
void remove(int index);
void showAll();
void run();
};
}
// 9_main.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_9;
int main() {
GraphicEditor editor;
editor.run();
return 0;
}
// 9_func.cpp
#include "9.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_9 {
void Shape::add(Shape* p) { this->next = p; }
void Line::draw() { cout << "Line" << endl; }
void Rect::draw() { cout << "Rect" << endl; }
void Circle::draw() { cout << "Circle" << endl; }
int UI::menu() {
int m; cout << "삽입(1), 삭제(2), 모두보기(3), 종료(4)>>"; cin >> m; return m; }
int UI::shapeType() { int t; cout << "선(1), 사각형(2), 원(3)>>"; cin >> t; return t; }
int UI::deleteIndex() { int i; cout << "삭제하고자 하는 도형의 인덱스>>"; cin >> i; return i; }
void GraphicEditor::insert(int type) {
Shape* p = nullptr;
if (type == 1) p = new Line();
else if (type == 2) p = new Rect();
else p = new Circle();
if (!pStart) pStart = pLast = p;
else { pLast->add(p); pLast = p; } }
void GraphicEditor::remove(int index) {
Shape *prev = nullptr, *curr = pStart;
for (int i = 0; i < index && curr; i++) prev = curr; curr = curr->getNext();
if (!curr) { cout << "삭제할 수 없습니다." << endl; return; }
if (prev) prev->add(curr->getNext());
else pStart = curr->getNext();
if (curr == pLast) pLast = prev; delete curr; }
void GraphicEditor::showAll() {
Shape* p = pStart;
for (int i = 0; p; i++) { cout << i << ": "; p->draw(); p = p->getNext(); } }
void GraphicEditor::run() {
cout << "그래픽 에디터입니다." << endl;
while (true) { int m = UI::menu();
if (m == 1) insert(UI::shapeType());
else if (m == 2) remove(UI::deleteIndex());
else if (m == 3) showAll();
else if (m == 4) break; } }
}
// [9장 정리문제]
// 1) 함수 재정의와 함수 오버라이딩의 차이를 설명하라
// 함수 재정의 vs 함수 오버라이딩
// 함수 재정의는 부모의 일반 함수를 자식 클래스에서 단순히 다시 만드는 것으로, 컴파일 시점에 포인터의 타입에 따라
호출될 함수가 결정된다.
// 함수 오버라이딩은 부모의 가상 함수를 자식에서 재구현하는 것으로, 실행 시점에 실제 객체의 타입을 확인하여 함수를
호출하는 동적 바인딩이 사용된다.
// 이 과정에서 데이터 영역에 가상 함수 테이블이라는 함수 주소 묶음을 만들어 객체 내부의 가상 함수 포인터가 이를
참조하게 함으로써 객체의 타입에 따른 동작을 만들어낸다.
// 2) 함수 오버라이딩과 함수 오버로딩의 차이를 설명하시오
// 함수 오버로딩은 한 클래스 내에서 이름은 같지만 매개변수의 타입이나 개수가 다른 함수를 여러 개 만드는 것이다.
따라서 컴파일 시점에 호출할 함수가 정해진다.
// 함수 오버라이딩은 상속 관계에서 부모의 가상 함수를 자식 클래스가 이름과 매개변수까지 똑같이 재구현하는 것으로
이는 실행 시점에 실제 객체의 타입에 따라 실행되는 함수를 결정하는 동적 바인딩을 통해 다형성을 구현하는 것이다.
// 따라서 오버로딩은 이름만 같은 다른 함수들을 모아놓은 것이라서 매개변수가 달라야 하지만, 오버라이딩은 부모의 기능을 완전히 덮어쓰는 것이기 때문에 매개변수까지 완벽히 일치해야 한다. 오버로딩이 확장이라면, 오버라이딩은 객체지향의
핵심인 유연성을 위한 것이다.
// 3) 업캐스팅이 왜 필요한지 설명하시오. 업캐스팅이 지원되지 않는다면 어떤 불편한 점이 발생하는지 생각해보라
// 업캐스팅의 가장 큰 필요성은 부모 타입의 포인터나 참조자 하나로 서로 다른 자식 객체들을 가리킬 수 있는 통합 관리의
효율성에 있다. 따라서 공통된 인터페이스(기본 클래스)를 통해 수많은 객체를 단 한 줄의 코드로 제어할 수 있게 된다.
// 업캐스팅이 지원되지 않는다면 서로 다른 파생 클래스의 객체들을 하나의 배열이나 리스트에 담을 수 없어 파생 클래스의
종류별로 수십 개의 배열을 따로 만들어야 한다. 또한, 새로운 파생 클래스가 하나 추가될 때마다 그 타입을 처리하기 위한
전용 함수를 매번 새로 만들거나, 기존의 거대한 if-else 조건문을 전부 찾아가 수정해야 해 유지보수가 어려워지며 코드의
중복은 폭발적으로 늘어나고, 작은 변화에도 전체 시스템을 수정해야 하는 구조가 되어 객체지향의 이점을 전혀 누릴 수
없게 된다.
// 4) 가상함수의 의미를 설명하시오
// 가상 함수는 부모 클래스에서 선언하지만 자식 클래스에서 반드시 재정의할 함수로, 프로그램의 결정 권한을 컴파일 시점이 아닌 실행 시점으로 넘기는 동적 바인딩의 핵심 도구이다.
// 가상 함수의 진정한 의미는 포인터라는 형식에 갇히지 않고 객체(데이터)의 실체에 맞는 동작을 보장한다. 부모 타입의
포인터로 호출하더라도, 가상 함수는 내부적으로 가상 함수 테이블을 참조하여 실제 생성된 객체가 누구인지 끝까지 추적해
그에 맞는 함수를 실행하고, 이러한 가상 함수는 다형성을 실현하는 수단이 된다.
// 개발자는 부모 클래스라는 하나의 인터페이스만 보고 코드를 짤 수 있게 되고, 나중에 어떤 새로운 자식 클래스가
추가되더라도 기존 코드를 수정할 필요 없이 각 객체가 자신만의 개성 있는 동작을 수행하도록 만들 수 있게 된다.
// 따라서 가상 함수는 소프트웨어의 유연함과 확장성을 극대화하여, 거대한 시스템을 효율적으로 관리할 수 있게 해준다.
// 5) 동적바인딩과 정적바인딩이 무엇인가
// 정적 바인딩은 컴파일 시점에 호출될 함수의 주소가 이미 결정되는 방식으로, 소스 코드상의 포인터나 변수의 타입에
의존하여 함수가 연결된다.
// 이는 일반 함수나 오버로딩된 함수에서 일어나며, 실행 시 별도의 주소 탐색 과정이 없어 성능 면에서 매우 효율적이지만
포인터가 가리키는 실제 객체가 무엇이든 상관없이 선언된 타입의 함수만 호출하기 때문에, 상속 관계에서의 유연한 동작은
어렵다.
// 동적 바인딩은 프로그램이 실행되는 도중에 실제 객체의 정체를 확인하여 호출할 함수의 주소를 결정하는 방식으로 부모
클래스에서 virtual로 선언된 함수를 호출할 때 발생하며, 내부적으로는 가상 함수 테이블을 참조하여 실제 객체의 함수를
찾아간다.
// 이러한 동적 바인딩은 실행 시점에 약간의 오버헤드를 발생시키지만, 하나의 인터페이스로 다양한 자식 객체들을 각자의
개성에 맞게 동작시킬 수 있는 다형성을 실현하는 핵심적인 기반 기술이 된다.
// 6) 동적바인딩의 장점은 무엇인가
// 동적 바인딩의 가장 큰 장점은 코드의 유연성과 확장성을 극대화한다는 점이다. 개발자는 구체적인 자식 클래스의 종류를
일일이 알 필요 없이, 부모 클래스의 인터페이스만으로도 여러 객체를 통합하여 제어할 수 있다. 이는 새로운 기능이나
클래스가 추가되더라도 기존의 관리 로직을 전혀 수정할 필요가 없게 만들어주는 객체지향 설계 원칙을 실현하는 핵심 작동
시스템이 된다.
// 또한 코드의 유지보수 비용을 획기적으로 낮춘다. 정적 바인딩만 사용한다면 객체의 타입이 바뀔 때마다 해당 타입을
처리하는 코드를 새로 작성해야 하지만, 동적 바인딩 환경에서는 객체가 스스로 자신의 행동을 결정하기 때문에 프로그램을
더 단순화하고, 유연하여 거대한 시스템에서도 각 객체가 독립적으로 동작할 수 있는 토대를 마련해 준다.
// 7) 업캐스팅할때 동적바인딩이 필요한 이유를 설명하라
// 업캐스팅을 하면 부모 타입의 포인터로 자식 객체를 가리키게 되는데, 이때 정적 바인딩만 작동한다면 컴파일러는 포인터의 부모 타입만 보고 무조건 부모의 함수만 실행하게 되어 자식 클래스의 오버라이딩 함수가 완전히 무시되어 버리지만,
// 동적 바인딩이 개입하여 실행 시점에 포인터가 가리키는 객체를 확인하여, 그 객체가 가진 고유한 함수 주소를 찾아내어
호출해 줌으로써 여러 자식 객체를 부모라는 이름으로 묶어서 관리하면서도, 동시에 각 객체가 가진 고유의 오버라이딩된
기능을 잃지 않게 하기 위해 반드시 동적 바인딩이 필요하다.
// 8) 가상함수테이블에 대하여 조사하고 이것을 이용하여 동적바인딩을 구현하는 방법을 설명하라
// 1. 가상 함수 테이블은 가상 함수를 하나라도 포함하고 있는 클래스에 대해 컴파일러가 생성하는 함수 주소 묶음으로,
프로그램의 읽기 전용 데이터 영역에 저장된다. 또한 테이블의 각 칸에는 해당 클래스가 호출할 수 있는 가상 함수의 실제
메모리 주소가 저장된다.
// 상속 관계에서 자식 클래스가 부모의 가상 함수를 오버라이딩하면, 자식 클래스의 테이블에서는 부모 함수의 주소가 있던
자리가 자식이 새로 만든 함수의 주소로 교체되어 각 클래스는 자신에게 맞는 함수 주소 목록을 독립적으로 보유하게 된다.
// 2. 동적 바인딩을 구현하기 위해 컴파일러는 객체 내부에 가상 함수 포인터라는 멤버를 추가하는데, 먼저 객체가 생성될 때 객체 내부의 vptr은 해당 클래스의 고유한 vtable 주소를 가리키도록 설정되고, 가상 함수 호출문을 만나면, 컴파일러는
포인터가 가리키는 객체의 vptr을 먼저 확인한다.
// 그 후 객체의 타입을 체크하여 실행 시점에 vptr이 가리키는 vtable로 이동하여, 호출하려는 함수의 인덱스에 저장된 실제
주소를 읽어와 함수 코드를 실행한다.
|
|
