[심화프로그래밍] Week6

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

실습 보고서 작성 요령


문제분석

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

프로그램 설계 및 알고리즘

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

소스코드 및 주석

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

결과 및 결과 분석

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

소감

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

Prob 1

문제분석

입력 받은 문자열을 역순으로 출력하는 프로그램이다. string 클래스를 사용해야하므로 string 변수를 선언하고 공백을 포함하여 입력 받아야하기 때문에 getline을 이용해 문자열을 입력 받는다. 역순 출력을 위해서는 입력 받은 문자열의 크기를 알아야하기 때문에 length() 함수를 이용해 문자열의 크기를 구한다. 이 크기를 이용하여 마지막 index부터 처음 index까지 반복문을 이용해 역순으로 출력하면 된다.

프로그램 설계 및 알고리즘

  1. string 변수를 선언하고 getline을 이용해 문자열을 입력 받는다.
  2. length() 함수를 이용해 입력 받은 문자열의 크기를 구한다.
  3. 문자열의 크기가 len이라고 할 때 마지막 index는 len-1, 처음 인덱스는 0이라는 점을 감안하여 변수 i를 len-1부터 0까지 하나씩 줄여나가며(i--) 출력한다.

소스코드 및 주석

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

int main(){
    string str; // string class 변수 선언
    int len; // 문자열 길이 변수

    cout << "문자열 입력 : "; 

    getline(cin, str); // 입력 받은 문자열 그대로 str string 변수에 저장
    len = str.length(); // str의 길이를 len에 저장

    cout << "원본 문자열 : " << str << endl; // 원본 문자열 출력
    cout << "역순 문자열 : ";
    for (int i = len-1; i >= 0; i--){ // 역순, 문자열의 마지막 index는 len-1, 처음 index는 0이다.
        cout << str[i]; // str의 마지막(str[len-1])문자부터 처음(str[0])까지 출력
    }
    cout << endl;
}

결과 및 결과 분석


getline을 이용해 string 변수에 문자열을 입력 받았다. getline을 이용했기 때문에 원본 문자열 출력 결과 공백까지 잘 포함되어 있는 것을 확인할 수 있다. 또한 역순으로도 잘 출력되는 것을 확인했다.

소감

상당히 간단한 과제였다. 지금까지 과제를 해오면서 string 클래스를 굉장히 유용하게 사용했어서 큰 어려움이 없었던 것 같다. 사실 객체지향언어를 처음 접하고 string을 사용할 때는 int, double과 같은 자료형을 사용할 때와 같아 string이 자료형인 줄 알았는데 클래스라는 것을 알게되면서 string 뒤에 붙는 문자가 단순히 변수가 아니라 객체임을 깨닫게 되었다.


Prob 2

문제분석

문자열을 입력 받고 대소문자를 구별하여 대문자는 소문자로, 소문자는 대문자로 바꿔 출력하는 프로그램이다. 대소문자 구분은 아스키코드값을 이용해야 할 것으로 보인다. 먼저 string 변수를 선언하고 getline을 이용해 공백을 포함한 문자열을 입력 받는다. 문자열의 모든 index를 보면서 대소문자를 체크해야하기 때문에 length를 이용해 문자열의 크기를 구한다. 문자열의 크기를 len이라고 할 때 len만큼 반복하며 아스키코드값이 소문자에 포함된다면 toupper을 이용해 대문자로 바꾸고, 아스키코드값이 대문자에 포함된다면 tolower을 이용해 소문자로 바꿔 출력한다.

프로그램 설계 및 알고리즘

  1. string 변수를 선언하고 getline을 이용해 문자열을 입력 받는다.
  2. length() 함수를 이용해 입력 받은 문자열의 크기를 구한다.
  3. 각 문자의 아스키코드값이 대문자(65이상 90이하)에 포함된다면 tolower을 이용해 소문자로 바꾸고, 아스키코드값이 소문자(97이상 122이하)에 포함된다면 toupper을 이용해 대문자로 바꾸는 조건문을 작성한다.
  4. 문자열의 크기가 len이라고 할 때 len만큼 반복하며 변환한다.
  5. 최종적으로 변환된 문자열을 출력한다.

소스코드 및 주석

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

int main(){
    string str; // string class 선언
    int len; // 문자열 길이 변수

    for(int j = 0; j < 3; j++){
        cout << "문자열 입력 : ";
        getline(cin, str); // 입력 받은 문자열 그대로 str string 변수에 저장
        len = str.length(); // str의 길이를 len에 저장

        for (int i = 0; i < len; i++){ // 문자열 처음 index부터 마지막 index까지
            if ((str[i] >= 65) && (str[i] <= 90)){ // 아스키코드 값이 65 아상 90 이하이면(대문자이면)
                str[i] = tolower(str[i]); // 해당 문자를 소문자로 바꾼다
            }
            else if((str[i] >= 97) && (str[i] <= 122)){ // 아스키코드 값이 97 아상 122 이하이면(대문자이면)
                str[i] = toupper(str[i]); // 해당 문자를 대문자로 바꾼다
            }
        }
        cout << "대소문자 변경 : " << str << endl << endl; // 대소문자 변경 문자열 출력
    }

}

결과 및 결과 분석


같은 프로그램을 3번 실행하도록 반복문을 작성했다. getline을 이용해서 공백까지 모두 잘 입력 받는 것을 확인할 수 있다. 대소문자가 변경 출력에도 소문자는 대문자로, 대문자는 소문자로 잘 변환하는 것을 볼 수 있으며 공백도 아스키코드 값을 갖지만 공백의 아스키코드값은 32로 조건문의 영향을 받지 않을 뿐더러 toupper, tolower 함수에 영향을 받지 않으므로 그대로 출력됨을 확인할 수 있다.

소감

이 문제도 큰 어려움 없이 원활하게 해결했다. string 클래스가 없었더라면 반복문을 이용해 문자열 길이를 체크하는 번거로운 과정을 거쳐야 했을텐데, string 클래스의 length 멤버함수로 쉽게 구할 수 있다는 점에서 string 클래스가 강력함을 느꼈다. 이번 문제는 오류없이 한번에 성공해서 약간의 희열을 느낄 수 있었다.


Prob 3

문제분석

구의 반지름을 입력 받고 부피를 계산하여 부피가 765cm3 이상인 구와 미만인 구의 개수를 출력하는 프로그램이다. this 포인트를 사용하기 위해 멤버 변수 이름과 매개변수 이름이 같은 변수가 존재해야 하고, 생성자와 소멸자를 사용해야하기 때문에 클래스 생성 후 생성자, 소멸자, 부피 계산 함수를 만들면 될 것으로 보인다. 다음으로는 각 구의 반지름을 받아야하는데, 이 때 포인터형 객체 생성 후 매개변수를 가진 객체를 동적 할당하면 될 것으로 보인다. 반지름을 받은 후에는 부피를 계산하여 765cm3 이상인지 아닌지를 판단해야하기 때문에 반지름을 매개변수로 갖는 부피 계산 함수를 생성하고 부피값을 반환한다. 반환값은 main함수에서 조건문을 거쳐 765cm3보다 크다면 변수 a를 1씩 증가시켜 개수를 체크하면 된다. 체크 후에는 최종적으로 부피가 765cm3 이상인 구, 미만인 구의 개수를 출력하고 동적 할당을 해제하면 될 것으로 보인다.

프로그램 설계 및 알고리즘

  1. Sphere 클래스 생성 후 객체 번호, 반지름, pi값을 private으로 선언하고 생성자, 소멸자, 부피 계산 함수를 public으로 선언한다. 함수는 클래스 외부에서 구현할 것이므로 선언만 진행한다.
  2. (Sphere::Sphere(int i)) 객체 번호를 매개변수로 갖는 생성자이다. 멤버 변수에 똑같이 i라는 이름을 가진 변수가 존재하므로 this 포인트를 사용하여 구별하고 객체 생성 멘트를 출력한다.
  3. (Sphere::~Sphere()) 객체 소멸시 객체 번호와 함께 객체 소멸 멘트를 출력한다.
  4. (int Sphere::Volume(int radius)) 반지름을 매개변수로 받아 부피를 계산하고 int형으로 반환하는 함수이다.
    volume = (4/3) x pi x r^3에서 r^3을 계산하기 위해 pow라는 함수를 사용했다. pow함수는 cmath라는 라이브러리에 존재하는 함수이므로 상단에 include 해준다. 계산한 부피는 반환한다.
  5. (main()) 기존 문제에서 조금 추가하여 구의 개수를 직접 사용자로부터 입력 받으려고 하기 때문에 개수를 저장할 변수, 반지름 변수, 구의 개수를 셀 변수를 선언한다. 구의 개수를 입력 받고, 개수 크기만큼의 포인터형 객체 배열을 선언한다. 이후 생성자 호출을 위해 반복문을 통해 동적할당한다. 다음으로는 반지름을 입력하고 부피를 계산해야하므로 또 다른 반복문을 이용해 반지름 입력하고 함수를 호출한다. 여기에서 반환값와 765를 비교해야하므로 조건문을 이용하여 조건을 만족하는 총 구 개수인 a를 설정한다. 최종적으로 765cm3 이상인 구와 미만인 구를 출력하고 다른 반복문으로 delete를 사용하여 소멸자를 호출한다.

소스코드 및 주석

#include <iostream>
#include <cmath> // pow함수 사용을 위한 라이브러리
using namespace std;

class Sphere{ // Sphere 클래스 생성
private: // private 접근 지정자
    int volume; // 부피 변수
    int i; // 객체 번호 변수
    double pi = 3.14159265358979323846;
public: // public 접근 지정자
    Sphere(int i); // 객체 번호를 매개변수로 갖는 생성자
    int Volume(int radius); // 반지름을 매개변수로 받고 부피를 계산하여 반환하는 함수
    ~Sphere(); // 소멸자
};

Sphere::Sphere(int i){ // 생성자
    this->i = i; // this 포인트
    cout << "구 객체 생성 " << i << endl; // 객체 생성 멘트 출력
}

Sphere::~Sphere(){ // 소멸자
    cout << "구 객체 소멸 " << i << endl; // 객체 소멸 멘트 출력
}

int Sphere::Volume(int radius){ // 부피 계산 및 반환 함수
    volume = (4.0/3.0)*pi*pow(radius,3); // 반지름을 받아 부피 계산
    return volume; // 부피값 반환
}

int main(){
    int count; // 구 개수 변수
    int radius; // 반지름 변수
    int a = 0; // 부피가 765cm3 이상인 구의 개수를 세는 변수
    cout << "구 개수 입력 : ";
    cin >> count; // 구 개수 입력
    cout << endl;

    Sphere *circle[count]; // 포인터형 객체 배열 생성
    
    for (int i = 1; i <= count; i++){
        circle[i] = new Sphere(i); // 동적 할당
    }
    cout << endl;
    for (int i = 1; i <= count; i++){
        cout << "구 " << i << "의 반지름 >> ";
        cin >> radius; // 반지름 입력
        // cout << circle[i] -> Volume(radius) << endl; 
        // 객체 배열을 Volume함수에 접근시켰을 때 계산값이 잘 반환되는지 확인
        if (circle[i] -> Volume(radius) >= 765){ // 부피 반환값이 765보다 크면
            a++; // a를 1 증가시킨다
        }
    }

    cout << endl << "구의 부피가 765cm3 이상인 구는 " << a << "개 입니다." << endl; // 부피가 765cm3 이상인 구 개수 출력
    cout << "구의 부피가 765cm3 미만인 구는 " << count-a << "개 입니다." << endl << endl; // 부피가 765cm3 미만인 구 개수 출력
    
    for (int i = count; i >= 1; i--){
        delete circle[i]; // 객체 소멸
    }
}

결과 및 결과 분석


먼저 추가 기능이었던 구의 개수를 입력하고 그 개수만큼 구 객체가 생성되는 것을 볼 수 있다. 다음으로는 구 객체 번호에 맞게 반지름을 입력 받고, 입력될 때마다 부피를 계산해서 조건에 맞다면 변수 a를 증가시키기 때문에 조건에 맞는 개수가 잘 출력되는 것을 확인할 수 있다. 조건에 맞지 않는 구의 개수는 총 입력 개수(7)에서 조건에 맞는 구의 개수(4)를 뺴서 계산했다. 마지막으로 반복문을 사용하여 역순 소멸까지 잘 진행된 것을 확인할 수 있다.

소감

지금까지 과제 문제 중 가장 오래 걸린 문제였다. 아직 객체를 다루는 실력이 부족해서 그런 것도 있지만 생성자, 반지름 입력 및 함수 호출, 소멸자에 대한 각각의 단계가 모두 다른 for문으로 묶여 있어서 비효율적이라는 생각에 조금 더 효율적으로 짤 수 있는 방법을 고민하다가 시간이 많이 걸린 것 같다. 특히 이번 문제에서는 사소한 오류도 굉장히 많았는데 그런 만큼 몰랐던 점들을 많이 배운 것 같다. 객체 선언 및 동적 할당을 for문 안에서 해버려서 해당 for문을 벗어나면 객체를 이용하지 못하는 문제가 있었다. 동적할당은 for문 안에서 하더라고 처음 객체를 선언할 때는 바로 main에서 선언해줘야 한다는 것을 배웠다. 또 이상한 실수로 시간이 많이 소요되기도 했다. 조건에 만족하는 구의 개수를 세기 위해서 a라는 변수를 두고 조건에 만족하면 a++을 통해 개수를 셌는데, 출력 결과 전혀 이상한 숫자가 나왔다. 이유를 몰라 Volume함수에서 계산 방식이 잘못되었나 계산 결과를 출력해보는 등 여러 방법을 시도해봤지만 별다른 문제가 없었다. 진짜 원인은 간단했다. 처음 a를 선언하고 초기화를 해주지 않아 쓰레기값이 출력되는 것이었다. 0으로 초기화 해주니 정확하게 잘 나오는 것을 확인할 수 있었다. 이번 과제를 통해 올바르 객체 선언 방법, 초기화 중요성 등 많은 것을 배웠지만 실제로 이런게 시험 문제로 나오면 바로 해결할 수 있을까하는 걱정이 든다.


Reference

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

0개의 댓글