|
|
[5장 연습문제]
// 5.h
#ifndef HEADER_5_H
#define HEADER_5_H
#include <string>
namespace header_5 {
(소스 코드)
}
// 5_main.cpp
#include "5.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_5;
(소스 코드)
// 5_func.cpp
#include "5.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_5 {
(소스 코드)
}
// 1
// 5.h
void half(double& num);
// 5_main.cpp
int main() {
double n = 20;
half(n);
cout << n;
}
// 5_func.cpp
void half(double& num) { num = num / 2.0; }
// 2
// 5.h
bool bigger(int a, int b, int& big);
// 5_main.cpp
int main() {
int a, b, big;
cout << "정수 2개 입력:";
cin >> a >> b;
if (bigger(a, b, big)) cout << "두 정수는 같습니다.";
else cout << "큰 수는 " << big;
}
// 5_func.cpp
bool bigger(int a, int b, int& big) {
big = (a > b) ? a : b;
return (a == b) ? true : false;
}
// 3
// 5.h
class Circle {
int r;
public:
Circle(int r);
int getr();
void setr(int r);
void show();
};
void increase(Circle &a, Circle &b);
// 5_main.cpp
int main() {
Circle x(10), y(5);
increase(x, y);
x.show();
}
// 5_func.cpp
Circle::Circle(int r) { this->r = r; }
int Circle::getr() { return r; }
void Circle::setr(int r) { this->r = r; }
void Circle::show() { cout << "반지름이 " << r << "인 원" << endl; }
void increase(Circle& a, Circle& b) {
int r = a.getr() + b.getr();
a.setr(r);
}
// 4
// 5.h
char& find(char* str, char x, bool& ls);
// 5_main.cpp
int main() {
char s[] = "Mike";
bool b = false;
char& loc = find(s, 'M', b);
if (b == false) cout << "미발견" << endl;
else loc = 'm';
cout << s << endl;
}
// 5_func.cpp
char& find(char* str, char x, bool& ls) {
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] == x) {
ls = true; return str[i];
}
}
ls = false; return str[0];
}
// 5
// 5.h
class Intstack {
int p[10] = { 0 };
int top = 0;
public:
bool push(int n);
bool pop(int& n);
};
// 5_main.cpp
int main() {
Intstack a;
for (int i = 0; i < 11; i++) {
if (a.push(i)) cout << i << ' ';
else cout << endl << i + 1 << " 번째 stack full" << endl;
}
int n;
for (int i = 0; i < 11; i++) {
if (a.pop(n)) cout << i << ' ';
else cout << endl << i + 1 << " 번째 stack empty";
}
cout << endl;
}
// 5_func.cpp
bool Intstack::push(int n) {
if (top == 10) return false;
p[top++] = n; return true;
}
bool Intstack::pop(int& n) {
if (!top) return false;
n = p[--top]; return true;
}
// 6
// 5.h
class Intstack2 {
int* p;
int size, top = 0;
public:
Intstack2(int n);
Intstack2(Intstack2& other);
bool push(int n);
bool pop(int& n);
~Intstack2();
};
// 5_main.cpp
int main() {
Intstack2 a(10);
a.push(10);
a.push(20);
Intstack2 b = a;
b.push(30);
int n;
a.pop(n);
cout << "a:" << n << endl;
b.pop(n);
cout << "b:" << n << endl;
}
// 5_func.cpp
Intstack2::Intstack2(int n) {
size = n; p = new int[size] {0};
}
Intstack2::Intstack2(Intstack2& other) {
this->size = other.size;
this->top = other.top;
this->p = new int[this->size];
for (int i = 0; i < this->size; i++) {
this->p[i] = other.p[i];
}
}
Intstack2::~Intstack2() { delete[] p; }
bool Intstack2::push(int n) {
if (top == 10) return false;
p[top++] = n; return true;
}
bool Intstack2::pop(int& n) {
if (!top) return false;
n = p[--top]; return true;
}
// 7
// 5.h
class Accu {
int val;
public:
Accu(int v);
Accu& add(int n);
int get();
};
// 5_main.cpp
int main() {
Accu acc(10);
acc.add(5).add(6).add(7);
cout << acc.get();
}
// 5_func.cpp
Accu::Accu(int v) { val = v; }
Accu& Accu::add(int n) { val += n; return *this; }
int Accu::get() { return val; }
// 8
// 5.h
class Buffer {
std::string txt = "";
public:
Buffer(std::string txt);
void add(std::string next);
void print();
};
Buffer& append(Buffer& buf, std::string txt);
// 5_main.cpp
int main() {
Buffer buf("hello");
Buffer& tamp = append(buf, "guys");
tamp.print();
buf.print();
}
// 5_func.cpp
Buffer::Buffer(string txt) { this->txt = txt; }
void Buffer::add(string next) { txt += next; }
void Buffer::print() { cout << txt << endl; }
Buffer& append(Buffer& buf, string txt) {
buf.add(txt); return buf;
}
// 9
// 5.h
class Book {
std::string title = "";
int price;
public:
Book(std::string title, int price);
void set(std::string title, int price);
void show();
};
// 5_main.cpp
int main() {
Book cpp("명품C++", 10000);
Book java = cpp;
java.set("명품자바", 12000);
cpp.show();
java.show();
}
// 5_func.cpp
Book::Book(const string title, int price) {
this->title = title; this->price = price; }
void Book::set(string title, int price) {
this->title = title; this->price = price; }
void Book::show() { cout << title << ' ' << price << "원\n"; }
// 10
// 5.h
class Dept {
int size;
int* scores;
public:
Dept(int size);
Dept(const Dept& dept);
~Dept();
int getsize();
void read();
bool isover60(int index);
};
int countpass(Dept dept)
// 5_main.cpp
int main() {
Dept com(10);
com.read();
int n = countpass(com);
cout << "60점 이상은 " << n << "명";
}
// 5_func.cpp
Dept::Dept(int size) {
this->size = size; this->scores = new int[size] {0}; }
Dept::Dept(const Dept& dept) {
this->size = dept.size; this->scores = new int[this->size];
for (int i = 0; i < this->size; i++) {
this->scores[i] = dept.scores[i]; }
}
Dept::~Dept() { delete[] scores; }
int Dept::getsize() { return size; }
void Dept::read() {
cout << size << "개의 점수 입력>> ";
for (int i = 0; i < size; i++) cin >> scores[i];
}
bool Dept::isover60(int index) {
if (index >= 0 && index < size) return scores[index] >= 60;
return false;
}
int countpass(Dept dept) {
int count = 0;
for (int i = 0; i < dept.getsize(); i++) {
if (dept.isover60(i)) count++;
}
return count;
}
// [5장 정리문제]
// 1) 참조변수를 설명하시오
// 참조변수는 이미 존재하는 변수의 메모리 공간을 가리키는 이름을 선언하는 것으로, 이때 포인터와 다른 점은 선언과
초기화가 같이 되어야 하고, 한번 선언된 이름은 다른 메모리 공간을 가리키도록 변경할 수 없다.
따라서 내부적으로는 포인터와 유사하게 동작하지만 반드시 초기화가 필요하고 가리키는 대상을 바꿀 수 없어 포인터보다
안전하며, 주로 함수 인자 전달 시 복사 비용을 절감하기 위해 사용한다.
// 2) 참조변수를 어떻게 활용하는가
// 참조변수는 주로 함수의 매개변수 단계에서 불필요한 데이터 복사를 방지하여 성능을 높이거나, 함수 내부에서 원본
데이터를 직접 조작해야 할 때 포인터 대신 활용한다.
// 3) 참조에 의한 호출의 장점을 설명하시오
// 참조에 의한 호출은 불필요한 복사를 없애 성능을 극대화하고, 함수 내에서 외부 원본을 직접 제어할 수 있게 하며,
포인터보다 문법이 간결하고 안전하다는 장점이 있다.
// 4) 리턴형을 참조형으로 선언하면 어떤 장점이 있는가
// 리턴형을 참조형으로 하면 불필요한 데이터 복사를 제거하여 성능을 높일 수 있으며 함수의 호출결과를 수정 가능한 변수로
활용할 수 있다.
그러나 함수 종료시 소멸되는 지역변수를 리턴할 경우 변수가 할당 해제될 수 있어 함수가 종료되어도 소멸되지 않는 변수만을 리턴해야 한다.
// 5) C++ 에서 객체의 대입연산은 어떻게 처리되는가
// 객체 대입은 이미 생성된 두 객체 사이의 데이터 복제이다. 기본적으로 컴파일러가 멤버별 복사(얕은 복사)를 수행하지만,
객체 내에 포인터가 포함된 경우 기존 메모리 해제 후 새로운 공간 할당 및 내용을 복제하여 깊은 복사 알고리즘을 작성한 후
연속 대입을 위해 자기 자신을 리턴하는 과정을 직접 구현해야 한다.
// 6) 함수가 객체를 리턴할때 컴파일러에 의해 자동으로 처리되는 2 가지는 무엇인가
// 함수가 객체를 값에 의해 리턴하면, 복사 생성자가 호출되어 메모리의 제3의 공간에 원본과 똑같은 임시 객체를 선언 및
복사(초기화)한다.
그 후 임시 객체를 만드는 데 성공하면 함수 내부에 있던 지역 객체는 소멸자를 호출하며 메모리에서 완전히 소멸한다.
// 7) 객체가 생성될 때 생성자 대신에 복사생성자가 자동으로 호출되는 3 가지 경우를 설명하시오
// 복사생성자는 객체가 생성될 때 멤버변수를 특정 값(객체 값)으로 초기화되며 할당될 때 자동으로 호출된다.
// 1. 이미 생성된 객체를 이용해 새로운 객체를 생성하고 초기화할 때 호출된다.
// 2. 함수의 인자로 객체를 넘길 때, 함수의 매개변수(새로운 객체)가 원본 객체를 복사하며 생성된다.
// 3. 함수에서 객체를 값으로 반환할 때 함수가 종료되면서 리턴하는 값을 전달하기 위해 '임시 객체'가 생성된다.
// 8) 위의 3 가지 경우에 생성자 대신 복사생성자를 자동으로 호출하는 이유를 설명하시오
// 이 3가지 상황은 모두 새로운 객체를 만들되, 그 내용은 기존 객체의 복사본이어야 하기 때문에 일반 생성자보다는
복사 생성자를 사용하여 할당하는 것이 효율적이다.
// 9) 위의 3 가지 경우는 서로 다르게 보이지만 사실상 동일한 일이 발생하는 경우이다 어떤 동작인지 설명하시오.
// 새로운 메모리 공간을 할당하고, 그 공간을 기존 객체의 멤버 변수 값들로 채워 넣으며 초기화하는 것이다.
[6장 연습문제]
// 6.h
#ifndef HEADER_6_H
#define HEADER_6_H
#include <string>
namespace header_6 {
(소스 코드)
}
// 6_main.cpp
#include "6.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
using namespace header_6;
(소스 코드)
// 6_func.cpp
#include "6.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
namespace header_6 {
(소스 코드)
}
// 1
// 6.h
int add(int* arr, int len, int* arr2 = 0);
// 6_main.cpp
int main() {
int a[] = { 1, 2, 3, 4, 5 };
int b[] = { 6, 7, 8, 9, 10 };
int c = add(a, 5), d = add(a, 5, b);
cout << c << endl << d << endl;
}
// 6_func.cpp
int add(int* arr, int len, int* arr2) {
int res = 0;
for (int i = 0; i < len; i++) res += arr[i];
if (arr2 != 0) {
for (int i = 0; i < len; i++) res += arr2[i];
}
return res;
}
// 2
// 6.h
class Person {
int id;
double weight;
std::string name;
public:
Person(int a = 1, std::string c = "Grace", double b = 20.5);
void show();
};
// 6_main.cpp
int main() {
Person grace, ashley(2, "Ashley"), helen(3, "Helen", 32.5);
grace.show();
ashley.show();
helen.show();
}
// 6_func.cpp
Person::Person(int a, string c, double b) {
id = a; name = c, weight = b; }
void Person::show() {
cout << id << ' ' << weight << ' ' << name << endl; }
// 3
// 6.h
int big(int a, int b, int max = 100);
// 6_main.cpp
int main() {
int x = big(3, 5), y = big(300, 60), z = big(30, 60, 50);
cout << x << ' ' << y << ' ' << z << endl;
}
// 6_func.cpp
int big(int a, int b, int max) {
int big = (a > b) ? a : b;
return (big > max) ? max : big;
}
// 4
// 6.h
class Vector {
int* mem;
int size;
public:
Vector(int m = 100, int s = 100);
~Vector();
void put(int index, int val);
int get(int index);
};
// 6_main.cpp
int main() {
Vector v, v2(50, 30);
v.put(10, 100);
cout << v.get(10) << endl << v2.get(10);
}
// 6_func.cpp
Vector::Vector(int m, int s) {
mem = new int[m] {0}; size = s;
for (int i = 0; i < m; i++) mem[i] = s;
}
Vector::~Vector() { delete [] mem; }
void Vector::put(int index, int val) { mem[index] = val; }
int Vector::get(int index) { return mem[index]; }
// 5
// 6.h
class transarr {
public:
static void itd(int source[], double base[], int len);
static void dti(double source[], int base[], int len);
};
// 6_main.cpp
int main() {
int x[] = { 1,2,3,4,5 };
double y[5], z[] = { 9.9,8.8,7.7,6.6,5.5 };
transarr::itd(x, y, 5);
for (int i = 0; i < 5; i++) cout << y[i] << ' ';
cout << endl;
transarr::dti(z, x, 5);
for (int i = 0; i < 5; i++) cout << x[i] << ' ';
cout << endl;
}
// 6_func.cpp
void transarr::itd(int source[], double base[], int len) {
for (int i = 0; i < len; i++) {
base[i] = (double)source[i]; }
}
void transarr::dti(double source[], int base[], int len) {
for (int i = 0; i < len; i++) {
base[i] = (int)source[i]; }
}
// 6
// 6.h
class arrutil {
public:
static int* concat(int a[], int b[], int len);
static int* remove(int a[], int b[], int len, int& resize);
};
// 6_main.cpp
int main() {
int x[5], y[5], len = 0;
cout << "정수를 5개 입력:";
for (int i = 0; i < 5; i++) cin >> x[i];
cout << "정수를 5개 입력:";
for (int i = 0; i < 5; i++) cin >> y[i];
cout << "합친 정수 배열 출력";
int* arr = arrutil::concat(x, y, 5);
for (int i = 0; i < 10; i++) cout << arr[i] << ' ';
cout << endl;
delete[] arr;
arr = arrutil::remove(x, y, 5, len);
cout << "배열 x에서 y를 뺀 배열, 개수는 " << len << endl;
for (int i = 0; i < len; i++) cout << arr[i] << ' ';
cout << endl;
}
// 6_func.cpp
int* arrutil::concat(int a[], int b[], int len) {
int* arr = new int[len * 2];
for (int i = 0; i < len; i++) arr[i] = a[i];
for (int i = 0; i < len; i++) arr[i+len] = b[i];
return arr;
}
int* arrutil::remove(int a[], int b[], int len, int& resize) {
int* arr = new int[len];
for (int i = 0; i < len; i++) arr[i] = a[i];
int count = len;
for (int i = 0; i < count; ) {
int j = 0;
while (j < len && arr[i] != b[j]) j++;
if (j < len) arr[i] = arr[--count];
else i++;
}
if (count == 0) { delete[] arr; return nullptr; }
resize = count;
int* finalArr = new int[count];
for (int i = 0; i < count; i++) finalArr[i] = arr[i];
delete[] arr;
return finalArr;
}
// 7
// 6.h
class Random {
public:
static void seed();
static void nexti(int min = 0, int max = 32767);
static void nextalpa();
static void nextd();
};
// 6_main.cpp
int main() {
Random::seed();
cout << "--- 정수 10개 (1~100) ---" << endl;
for (int i = 0; i < 10; i++) Random::nexti(1, 100);
cout << "\n\n";
cout << "--- 알파벳 10개 ---" << endl;
for (int i = 0; i < 10; i++) Random::nextalpa();
cout << "\n\n";
cout << "--- 실수 10개 (0.0~1.0) ---" << endl;
for (int i = 0; i < 10; i++) Random::nextd();
cout << endl;
}
// 6_func.cpp
void Random::seed() { srand((unsigned)time(0)); }
void Random::nexti(int min, int max) {
int r = rand() % (max - min + 1) + min;
cout << r << " ";
}
void Random::nextalpa() {
char c = (char)(rand() % 26 + 'a');
cout << c << " ";
}
void Random::nextd() {
double d = (double)rand() / RAND_MAX;
cout << d << " ";
}
// 8
// 6.h
class Trace {
private:
static std::string tag[100];
static std::string info[100];
static int head;
static int next;
static int count;
public:
static void put(std::string t, std::string i);
static void print(std::string t = "");
};
// 6_main.cpp
void f() {
int a, b, c;
cout << "2개의 정수를 입력하시오>>";
cin >> a >> b;
Trace::put("f()", "정수를 입력 받았음");
c = a + b;
Trace::put("f()", "합 계산");
cout << "합은 " << c << endl;
}
int main() {
Trace::put("main()", "프로그램 시작합니다.");
f();
Trace::put("main()", "종료");
Trace::print("f()");
Trace::print();
}
// 6_func.cpp
string Trace::tag[100];
string Trace::info[100];
int Trace::head = 0;
int Trace::next = 0;
int Trace::count = 0;
void Trace::put(string t, string i) {
tag[next] = t;
info[next] = i;
if (count == 100 && next == head) head = (head + 1) % 100;
next = (next + 1) % 100;
if (count < 100) count++;
}
void Trace::print(string t) {
if (t == "") cout << "---모든 정보 출력---\n";
else cout << "---" << t << "태그의 정보 출력---\n";
for (int i = 0; i < count; i++) {
int curr = (head + i) % 100;
if (t == "" || tag[curr] == t) {
cout << tag[curr] << ": " << info[curr] << endl;
}
}
}
// 9
// 6.h
// 9
class Board {
static std::string board[20];
static int line;
public:
static void add(std::string title);
static void print();
};
// 6_main.cpp
int main() {
Board::add("중간고사는 감독 없는 자율 시험입니다.");
Board::add("코딩 라운지 많이 이용해 주세요.");
Board::print();
Board::add("진소린 학생이 경진대회 입상하였습니다. 축하해주세요");
Board::print();
}
// 6_func.cpp
string Board::board[20];
int Board::line = 0;
void Board::add(std::string title) {
board[line++] = title; }
void Board::print() {
cout << "********** 게시판입니다. **********\n";
for (int i = 0; i < line; i++) {
cout << i << ": " << board[i] << endl;
}
}
// [6장 정리문제]
// 1) 디폴트 매개변수를 선언하는 위치는 어디인지 설명하시오
// 디폴트 매개변수는 함수의 선언부(헤더 파일)에 작성해야 하며, 매개변수 리스트의 맨 오른쪽(마지막) 인자부터 시작하여
왼쪽 방향으로 연속되게 정의해야 한다.
// 이는 컴파일러가 함수 호출 시 전달되지 않은 인자값을 선언부의 정보를 바탕으로 오른쪽부터 차례대로 채워 넣기 때문이다.
// 또한 이미 선언부에서 정의된 디폴트 값은 중복 정의 에러를 방지하기 위해 구현부에서는 생략한다.
// 2) 함수의 중복이 왜 필요한가
// 함수의 중복은 이름 하나로 여러 데이터를 처리할 수 있게 함으로써, 코드의 활용성과 직관성을 높여 준다.
// 3) static 키워드를 붙이는 위치는 어디인가 (선언 또는 정의)
// static 키워드는 클래스 내부의 선언부의 멤버함수 및 멤버변수에만 붙인다.
// 4) static 멤버변수는 왜 외부에서 다시 선언해야 하는 이유와 이때는 static 키워드를 붙이지 않는 이유를 설명하시오.
// 클래스 선언 내부의 static 변수는 실제 데이터를 저장할 메모리 공간이 할당되지 않는 설계도이기 때문이다.
// 따라서 실제 메모리 공간을 확보하는 과정이 클래스 외부에서 필요하게 된다.
또한 static 키워드는 "이것은 정적 멤버다"라는 속성을 알려주는 역할인데, 이미 클래스 설계도 내에서 그 속성이 정해졌기
때문에 외부 정의 시에 또 붙이게 되면 컴파일러가 이를 클래스 멤버가 아닌 파일 범위의 일반 정적 변수로 오해할 수 있기
때문이다.
|
|
