[심화프로그래밍] Week7

Yoons·2022년 10월 20일
0
post-thumbnail

실습 보고서 작성 요령


문제분석

  • 결과 화면을 캡쳐 하여 첨부, 해당 결과가 도출된 이유와 타당성 분석

프로그램 설계 및 알고리즘

  • 해결 방법에 따라 프로그램 설계 및 알고리즘 등 기술
  • e.g.) 문제 해결 과정 및 핵심 알고리즘 기술

소스코드 및 주석

  • 소스코드와 그에 해당하는 주석 첨부
  • 각각의 함수가 수행하는 작업, 매개변수, 반환 값 등을 명시
  • 소스코드 전체 첨부(소스코드 화면 캡처X, 소스코드는 복사/붙여넣기로 첨부)

결과 및 결과 분석

  • 결과 화면을 캡쳐 하여 첨부, 해당 결과가 도출된 이유와 타당성 분석

소감

  • 실습 문제를 통해 습득할 수 있었던 지식, 느낀 점 등을 기술

Prob 1

문제분석

등속도로 진행하는 선박의 속도를 이용하여 항구 진입 전과 진입 후의 속도를 각각 계산하여 출력하는 과제이다. 문제에서 각각 단위가 달라 하나의 단위로 변환하는 과정이 필요할 것으로 보이고, '3000킬로미터로 나누겠다'라고 했는데 3000km/h인지, 3000km/s인지 모호하지만 실행결과를 보면 km/s로 했으므로 모든 단위를 km/s로 변환하여 계산하려고 한다. 주소에 의한 호출을 사용해야하기 때문에 main함수에서 주소값을 매개변수로 갖는 함수를 호출하고 해당 함수에서는 포인터 변수를 받아 값을 계산한 후 출력한다. 항구 진입 전과 진입 후의 계산식이 다르므로 Before, After 함수를 각각 생성하려고 한다. 이때 함수 내의 변수는 float형을 사용한다.

프로그램 설계 및 알고리즘

(가정 : 모든 단위를 km/s로 환산 후 계산해본 결과 값이 이상하다고 판단되어 단위는 킬로미터 매 초로 통일되어있다고 생각하고 문제를 해결함)

1. 항구 진입 전 속도를 계산 할 Before 함수와 항구 진입 후 속도를 계산 할 After 함수를 선언한다.
2. main함수에서 '주소에 의한 호출'을 사용해야하므로 각가의 함수를 선언할 때 main함수로부터 받은 주소값을 포인터변수에 저장한다.
3. (Before(float defaultV)) 항구 진입 이전 속도를 계산하는 함수로써, 원래 운행 속도였던 defaultV의 주소값을 포인터 매개변수로 받고 3,000으로 나눈 후 float형 변수인 resultBefore에 저장하여 출력한다.
4. (After(float
defaultV)) 항구 진입 이후 속도를 계산하는 함수로써, 원래 운행 속도였던 defaultV의 주소값을 포인터 매개변수로 받고 10,000을 뺀 값에 3,000으로 나눈 후 float형 변수인 resultAfter에 저장하여 출력한다.

소스코드 및 주석

#include <iostream>
using namespace std;

void Before(float* defaultV){ // 항구 진입 전 선박 속도 계산 함수
    float resultBefore = *defaultV/3000; // 매개변수로 받은 값을 3,000으로 나눔
    cout << "선박 속도(항구 진입 전) : " << resultBefore << " km/s" << endl; // 항구 진입 전 선박 속도 계산 결과 출력
}

void After(float* defaultV){ // 항구 진입 후 선박 속도 계산 함수
    float resultAfter = (*defaultV-10000)/3000; // 매개변수로 받은 값에서 10,000을 뺴고 3,000으로 나눔
    cout << "선박 속도(항구 진입 후) : " << resultAfter << " km/s" << endl; // 항구 진입 후 선박 속도 계산 결과 출력
}

int main(){
    float defaultV = 17000; // 기본 속도는 17,000

    Before(&defaultV); // Before 함수에 default의 주소값 전달
    After(&defaultV); // After 함수에 default의 주소값 전달
}

결과 및 결과 분석


단위 변환과정을 따로 거치지 않았지만, 항구 진입 전과 후에 대해 문제에서 요구하는 형식과 값에 맞게 출력되는 것을 확인할 수 있다.

소감

조교님이 단위가 맞지 않으니 무시하고 풀라고 하셨지만, 물리를 많이하고 단위에 민감한 기계로봇에너지공학과에서 전과한 학생으로서 제대로 단위를 변환하고 정확한 값을 계산해보고 싶었다. 1노트가 1.875km/h라는 정보를 찾고 km/h를 km/s로 변환하니 17,000노트는 8.8542km/s라는 값이 나왔다. 이어서 '3,000킬로미터'가 km/s라면 말이 안되기 때문에(km/s라면 진입 전 속도로 0.0029514km/s가 나온다..) 따라서 3,000km/h라고 생각하고 km/s로 변환하면 0.8333km/s가 나온다. 따라서 17,000노트는 8.8542km/s이고 3,000km/h는 0.8333km/s이므로 진입 전 속도는 8.8542/0.8333를 하면 10.6255km/s가 나온다.. 초기 속도가 8.8542km/s였는데 계산 후 속도가 더 크게 나와서 그냥 문제에서 보여준 실행 결과대로 나오게 계산하기로 했다..조금 찝찝했다..!


Prob 2

문제분석

두 개의 숫자로 된 문자열을 받고 서로 문자열을 교환하는 과정과 각각의 문자열을 역순으로 배열하여 출력하는 문제이다. 함수에 의한 호출을 사용해야하므로 main함수에서 먼저 두 개의 문자열을 받고 그 두 개의 문자열을 매개변수로 하는 함수를 만들어야 한다. 이때 해당 함수에서는 이 두 문자열을 참조 형식으로 받아야 한다. 이후 받은 두 개의 문자열을 해당 함수에서 서로 교환하고, 각각의 문자열을 역순으로 배열하는 과정을 거치면 된다. 또한 문자열의 처음 값이 0이면 null로 바꿔야하기 때문에 해당 과정도 거쳐야한다.

프로그램 설계 및 알고리즘

  1. (main()) 두 개의 문자열을 string 변수로 선언하고 각각 입력 받는다. 이 때 공백은 포함하지 않을 것이므로 getline이 아닌 cin으로 입력 받았다.
  2. (main()) 1에서 받은 두 문자열을 매개변수로 하는 함수를 호출한다.
  3. (change(string& a, string& b)) 먼저 두 문자열을 상호 교환하는 과정을 거친다. 이 과정을 위해서는 임시 저장소인 temp라는 string 변수를 만들어 참조변수 a와 b에 저장된 문자열을 서로 바꾼다. 완료되면 교환 완료 메세지를 출력한다.
  4. (change(string& a, string& b)) 다음은 각각의 문자열에 첫 문자로 0이 있다면 해당 위치에 저장된 값을 null로 바꾼다. 다음으로는 문자열을 역순으로 재배치 하는 과정을 거쳐야 하는데, 그 방법으로 데칼코마니 처럼 중간값을 기준으로 반대편에 있는 값과 교환하는 과정을 거쳤다.
    .length()로 받은 문자열의 크기를 계산하여 lenA, lenB에 저장하고 각 크기의 절반씩 반복문을 실행한다. (데칼코마니처럼 진행되기 때문에 문자열의 전반부에 해당하는 절반만 있으면 된다.)
  5. (change(string& a, string& b)) 역순 배열이 완료되면 완료 메세지를 입력하고 최종 결과를 출력한다.

소스코드 및 주석

#include <iostream>
using namespace std;

void change(string& a, string &b){ // 문자열 2개를 참조 매개변수로 받고 서로 교환 및 역순배열하는 함수
    string temp; // 문자열 상호 교환 시 임시 저장 문자열 변수
    char tempE; // 역순 배열시 임시 저장 문자열 변수
    int lenA; // 문자열 a의 길이 변수
    int lenB; // 문자열 b의 길이 변수

    temp = b; // b를 temp에 저장
    b = a; // a를 b에 저장
    a = temp; // temp를 a에 저장

    cout << endl << "숫자를 서로 교환하였습니다." << endl; // 상호 교환 완료 메세지

    lenA = a.length(); // 문자열 a의 길이를 구하고 lenA에 저장
    lenB = b.length(); // 문자열 b의 길이를 구하고 lenB에 저장


    if (a[0] == '0'){ // 문자열 a에서 맨 처음 문자가 0이라면
        a[0] = '\0'; // null로 바꿈
    }

    for (int j = 0; j < lenA/2; j++){ // j가 a 문자열 길이의 절반만큼(소수점은 버림) 하나씩 증가하면서 실행
        tempE = a[j]; // 문자열 a의 j번 인덱스에 있는 문자를 tempE에 저장
        a[j] = a[lenA-j-1]; // 데칼코마니처럼 문자열의 가운데 문자를 기준으로 j번 인덱스의 반대편에 있는 문자를 j번 인덱스에 저장
        a[lenA-j-1] = tempE; // tempE에 있는 문자열을 j번 인덱스의 반대편에 있는 문자로 저장
        // ex. 12345
        // 가운데 문자가 3이므로 맨 앞 1은 3을 기준으로 반대에 있는 5와 교환해야함.
        // 맨 앞 숫자인 1을 tempE에 저장하고
        // 5를 1이 담겨있던 인덱스에 저장하고
        // tempE에 있는 문자를 5가 있는 인데스에 저장
    }

    if (b[0] == '0'){ // 문자열 b에서 맨 처음 문자가 0이라면
        b[0] = '\0'; // null로 바꿈
    }

    for (int j = 0; j < lenB/2; j++){ // j가 b 문자열 길이의 절반만큼(소수점은 버림) 하나씩 증가하면서 실행
        tempE = b[j]; // 문자열 a의 j번 인덱스에 있는 문자를 tempE에 저장
        b[j] = b[lenB-j-1]; // 문자열의 가운데 문자를 기준으로 j번 인덱스의 반대편에 있는 문자를 j번 인덱스에 저장
        b[lenB-j-1] = tempE; // tempE에 있는 문자열을 j번 인덱스의 반대편에 있는 문자로 저장
    }

    cout << "숫자를 거꾸로 변경하였습니다." << endl << endl; // 역순배열 완료 메세지

    cout << "민수의 숫자 : " << a << endl; // 바뀐 문자열 출력
    cout << "철수의 숫자 : " << b << endl;
}

int main(){
    string first; // 첫 번째 문자열
    string second; // 두 번째 문자열

    cout << "민수의 숫자 : ";
    cin >> first; // 민수 숫자 입력

    cout << "철수의 숫자 : ";
    cin >> second; // 철수 숫자 입력

    change(first, second); // 두 개의 문자열 first, second를 매개변수로 하여 change함수 호출
}

결과 및 결과 분석


결과를 보면 민수는 문자열에 0이 포함되어 있지만 맨 앞에 위치해 있지 않고, 철수는 0이 맨 앞에도 있고 뒤에도 있는 것을 볼 수 있다. 입력될 때 맨 앞 0에 대해서만 null로 바꾸기 때문에 철수가 입력한 098031은 98031로 바뀐 후 역순 정렬이 되어 출력됨을 확인할 수 있다. 민수가 입력한 숫자도 역순으로 잘 출력된 것을 확인할 수 있는데, 맨 뒤 0은 null로 바뀌지 않기 때문에 역순 배열 시 가장 앞에 오는 것을 확인 할 수 있다.

소감

참조에 의한 호출이라는 것이 익숙치 않아서 처음에는 어떻게 해야할지 고민이 많았지만, 그냥 이름만 바꿔서 하기를 원할 때 쓴다는 느낌을 받은 이후로 쉽게 문제를 해결할 수 있었다. 특히 이번 문제는 이전에 배웠던 조건문, 반복문, string 객체, .length(), 입출력 등 많은 것을 사용해봤다는 점에서 이전에 배운 내용을 한 번더 상기하며 문제를 풀 수 있었다. 특히 역순 배열 문제는 이전에도 한 번 풀어본 문제여서 기억을 회상하며 쉽게 풀 수 있었다.


Prob 3

문제분석

복사 생성자를 이용해서 객체를 복사하고 객체 내의 명령을 그대로 수행하는 문제이다. 복사 생성은 얕은 복사와 깊은 복사로 나눠지는데, 얕은 복사의 경우 주소 공유에 의해 에러가 발생할 수 있다. 따라서 깊은 복사를 이용해 문제를 해결해야한다. Factory 클래스를 만들고 생성자, 소멸자, 복사생성자를 선언한 뒤 클래스 외부에 구현 후 main함수로부터의 호출과정을 거친다.

프로그램 설계 및 알고리즘

  1. Factory 클래스를 생성한다.
  2. 문자열을 저장한 길이 변수와 포인터형 이름 변수를 선언한다.
  3. 생성자, 소멸자, 복사 생성자를 선언한다.
  4. (Factory(const char* name)) 생성자로, main 함수로부터 받은 문자를 포인터형으로 받는다. 문자열 크기 만큼의 동적할당을 위해 라이브러리 함수인 strlen함수를 이용해 문자열 크기를 계산하고 len에 저장한다. 문자열 뒤 null값을 포함하여 len+1만큼 멤버변수 name에 동적할당한다. 동적할당된 멤버 변수 name에 매개변수 name의 내용을 복사하기 위해 라이브러리 함수인 strcpy를 이용한다. 이후 "메모리 할당!"이라는 생성자 실행 메세지를 출력한다.
  5. (Factory(const Factory& m)) 복사 생성자로, main함수로부터 받은 객체를 참조 형식으로 받는다. 깊은 복사를 위해 기존 객체 생성자와 유사한 방식을 그대로 거쳐야한다. 다만 기존 객체의 데이터를 받아야하기 때문에 .(dot)를 이용하여 객체 데이터로 접근한다.
  6. (~Factory()) 소멸자로, return 0;이 입력되면 생성 순서의 역방향으로 소멸된다. "메모리 소멸"이라는 소멸 메세지를 출력한다.

소스코드 및 주석

#include <iostream>
#include <cstring> // strcpy, strlen 함수 사용을 위한 라이브러리
using namespace std;

class Factory{ // Factory 클래스 생성
private: // private 접근지정자
    int len; // 문자열 길이 변수
    char* name; // 이름 포인터 변수
public: // public 접근지정자
    Factory(const char* name); // 생성자 선언
    Factory(const Factory& m); // 복사 생성자 선언
    ~Factory(); // 소멸자 선언
};

Factory::Factory(const char* name){ // 이름 포인터 변수를 매개변수로 하는 생성자 구현
    len = strlen(name); // name의 길이를 계산하여 len에 저장
    this->name = new char[len+1]; // 문자열 길이+1의 크기를 갖는 char형 배열 메모리를 멤버변수 name에 동적할당
    strcpy(this->name, name); // 멤버변수 name에 매개변수 name을 복사
    cout << "메모리 할당" << endl; // 생성자 실행 메세지
}

Factory::Factory(const Factory& m){ // 객체 m을 매개변수로 하는 복사 생성자 구현
    len = strlen(m.name); // 객체 m에 있는 name의 길이를 구해 len에 저장
    this->name = new char[len+1]; // 문자열 길이+1의 크기를 갖는 char형 배열 메모리를 멤버변수 name에 동적할당
    strcpy(this->name, m.name); // 멤버변수 name에 매개변수 객체 m의 name을 복사
    cout << this->name <<" 1대 생산!!" << endl; // 복사 생성자 실행 메세지 출력
}

Factory::~Factory(){ // 소멸자ㄱ
    cout << "메모리 소멸" << endl; // 소멸자 실행 메세지
}

int main()
{
    
    Factory memory("Apple"); // memory 객체에 Apple이라는 매개변수와 함께 생성
    Factory newMemory(memory); // newMemory에 memory 객체를 복사 생성

    return 0; // 소멸자 호출, newMemory, memory 순서로 소멸
}

결과 및 결과 분석


Factory에 memory라는 객체를 선언하고 매개변수로 "Apple"라는 문자열을 넘겨줬다. 이 때 생성자가 생성되면서 "메모리 할당"이라는 메세지가 출력된다. 이후 newMemory 객체를 생성하고 매개변수로 memory라는 객체를 통채로 넘겨줬는데 이때는 생성자가 생성되지 않는다. 하지만 복사 생성자에 의해 객체가 복사되고 "Apple 1대 생산!!"이라는 출력을 확인할 수 있다. 이후 return 0;을 통해 newMemory, memory 순으로 소멸자가 실행되면서 "메모리 소멸"이라는 출력을 확인할 수 있다.

소감

얕은 복사, 깊은 복사에 대한 개념은 이해가 가지만 아직 잘 쓸 수 있을 정도는 아닌 것 같다. 처음에 복사생성자를 선언하면서 매개변수를 const로 받지 않아 오류가 발생했다. 레퍼런스를 통해 복사 생성자는 항상 const로 받아야한다는 내용을 참고하여 const로 받으니 오류가 해결됐다. 시험 직전이라 시험에 나오면 어떻게 빨리 해결하지라는 걱정이 들기도 한다. 이번 문제를 마지막으로 중간고사 전 모든 실습 내용을 마쳤는데, 처음부터 다시 돌아보면서 개념을 탄탄히 익힐 수 있도록 공부해야겠다.


Reference

profile
모든 글은 저의 눈물을 머금으며 작성한 글이니..재밌게 봐주세요 :) 깃헙 맞팔@

0개의 댓글