STUDY.md
  • C++ 7주차. Code Flowchart / Review.
    2024년 06월 02일 00시 04분 32초에 업로드 된 글입니다.
    작성자: 방세연

    Contents. 클릭하면 이동합니다. 🚀

       


       

       

      code Flowchart를 두 가지 방식으로 작성해보았다.

      첫 번째는 직접 흐름도를 작성하는 drow.io

      두 번째는 코드를 이용하면 flow chart를 그려주는 mermaid.live이다.

       

      먼저 그동안의 c++ 과제를 하며 작성해둔 3주차 코드를 도식화해보도록 하겠다.

       

       

      drow.io

       

       

      코드를 보는 가독성이 좋고, 깔끔하게 도식화하기에는 매우 좋은 툴인 것 같다.

      하지만 더 빠르고 간편한 방식으로 본인 혹은 상대의 코드 흐름도를 볼 수 있는 방법이 없을까 생각했고,

      이번에는 mermaid.live라는 툴 사이트를 발견했다.

       

       

       


      mermaid.js.org

       

      mermaid에서 사용할 수 있는 flowchart를 작성하려면

      이렇게 따로 사이트 내에서 식별할 수 있도록 규칙을 정해줘야 했다.

      이렇게 나열하면 매번 직접 블록을 설치할 필요 없이 스스로 블록을 정리해준다.

       

       

       



       계산기 프로그램 구조화 

       

      제공된 계산기 프로그램을 mermaid.live로 도식화 해보았다.

      기능을 선택해 계산하는 과정이 잘 담겨있다.

       

       


       계산기 프로그램 코드 리뷰 

      #include <iostream>
      #include <cmath>
      using namespace std;
      
      int main() {
          while (true) {
              double a, b, r; int c; cout << "=============================\n  공학용 계산기 프로그램\n=============================\n사용할 기능을 선택하세요:\n1. 더하기\n2. 빼기\n3. 곱하기\n4. 나누기\n5. 거듭제곱\n6. 제곱근\n7. 사인\n8. 코사인\n9. 탄젠트\n"; cin >> c;
              if (c == 1 || c == 2 || c == 3 || c == 4 || c == 5) { cout << "첫 번째 숫자 입력: "; cin >> a; cout << "두 번째 숫자 입력: "; cin >> b; }
              else if (c == 6 || c == 7 || c == 8 || c == 9) { cout << "숫자 입력: "; cin >> a; }
      
              switch (c) {
                  case 1: r = a + b; break;
                  case 2: r = a - b; break;
                  case 3: r = a * b; break;
                  case 4: if (b == 0) { cout << "0으로 나눌 수 없습니다.\n"; continue; } r = a / b; break;
                  case 5: r = pow(a, b); break;
                  case 6: if (a < 0) { cout << "음수의 제곱근은 계산할 수 없습니다.\n"; continue; } r = sqrt(a); break;
                  case 7: r = sin(a); break;
                  case 8: r = cos(a); break;
                  case 9: r = tan(a); break;
                  default: cout << "유효하지 않은 선택입니다.\n"; continue;
              }
      
              cout << "-----------------------------\n 결과: " << r << endl << "-----------------------------\n";
              char cont; cout << "계속 하시겠습니까? (y/n): "; cin >> cont;
              if (cont == 'n' || cont == 'N') break;
              cout << "=============================\n\n";
          }
          return 0;
      }



       

       

      이 코드를 해석해보겠다.

      while문을 통해 메뉴를 반복하고,

      공학용 계산기 프로그램 문구와 메뉴를 출력하는 코드가 나타나있다.

       

      가능한 피드백 1.
      공학용 계산기 프로그램 문구와 메뉴를 출력하는 창 코드가 보기 힘들게 구성되어있다.
      이 문구만 main루프에서 꺼내 따로 함수로 분류해주고, main()에 불러오는 식으로 구성해주어도 괜찮을 것이다.

       

       

      조건에 따라 a, b를 입력해주어 계산에 필요한 숫자를 프로그램에서 입력받도록 해준다.

       

      가능한 피드백 1.
      입력받은 c의 숫자를 범위를 표현해 작성해주면 더욱 간결해질 것이다.
      ex. if( c >= 1 && c <= 5 )
      가능한 피드백 2.
      입력받는 숫자가 a, b일때, 그리고 입력받는 숫자가 a일 때
      두 개의 숫자를 입력받는 부분과 하나의 숫자를 입력받는 부분에서 a가 중복된다.

      이를 각각 작성하지 않고 재사용할 수 있는 방안을 생각해볼 것이다.

      switch문을 통해 계산을 진행하는데,

      case 4에서 입력된 b의 값이 0일 경우 조건문 계산 불가 메세지를 출력한다.

       

      가능한 피드백 1.
      바로 위 단계의 if문과 이 switch문을 분리하면 가독성 낮아보이기 때문에,

      a, b를 모두 입력해야 하는 경우 (1 ~ 5 ) / a만 입력해야 하는 경우 ( 6 ~ 9)
      로 나누어 각각의 큰 틀 안에서 계산을 수행하는 것도 좋은 방법일 것 같다.

       

      숫자가 a 하나만 필요한 경우의 case문이다.

      이때에도 case 6에서 a가 음수일 때 음수의 제곱근은 계산할 수 없다는 문구가 출력되어야 한다.

       

      만약 입력받은 메뉴 숫자 c가 1 ~ 9의 범위를 벗어날 때 continue;를 통해 다시 메뉴로 돌아가도록 한다.

       

      마지막으로 메뉴 c와 숫자 a, b를 올바르게 입력했다면 볼 수 있는 코드이다.

      결과값이 출력되면서 계속 진행할 지의 여부를 판단할 수 있다.

       

      가능한 피드백 1.
      위의 y, n을 입력받는 부분도  main루프가 아닌 다른 함수로 분류해준다면 보기가 더욱 쉬워질 것이다.

       

      while문에서 벗어났을 때 프로그램은 종료된다.

       

       

       


       계산기 프로그램 코드 개선 

      다음은 위에서 코드 리뷰를 통해 아쉬웠던 점들을 개선해본 결과이다.

       

       

       

      전역함수() / MENU(), ABINPUT(), ORDER, CONT()

      MENU()
      ABINPUT()
      CONT()
      ORDER()

       

      1. 계산기의 기능에 따라 (MENU, CONT, ABINPUT, ORDER)로 전역 함수를 나눠 적어주었다.

      따라서 식별하기에 더 편할 것이다.

      2. MENU()에 나타나있는 '공학용 계산기 프로그램' '사용할 기능'을 보기에 더 편하도록 바꿔 작성했다.

      3. ABINPUT()에서는, a와 b의 입력 코드가 여러번 반복되는 것을 방지하기 위해 참조에 의한 호출(call by reference)을 사용해 메모리 사용량을 줄이고 하나의 함수 호출로 대체할 수 있도록 했다.

      (함수에 인수를 전달할 때 인수의 메모리 주소를 전달하여 원본 데이터에 직접 접근하게 했다.)

      4. bool을 사용한 CONT()를 통해서 y, Y 입력을 조금 더 간결하게 할 수 있도록 했다.

      5. ORDER()에서 if문을 적어주는 대신 switch문 내에서 case 1 ~ 5, case 6 ~ 9로 분류해 연산을 수행해 보기 더 쉽도록 했다.

       

       

      main()

       

      이제 main()함수를 적어보았는데, 매우 간결해진 것을 볼 수 있다.

      main()함수에 기능을 조금 더 남겨볼까 하다가 이번에는 main()을 가장 간결하게 작성해보는 쪽으로 방향을 잡아보았다.

       

      또한 이 함수에서도 개선된 부분이 있는데,

      마지막 if문의 조건이 CON()문의 부정일 때 (y, Y를 입력하지 않았을 때),

      바로 종료 문구를 출력하도록 하여 y, Y를 입력했을 때만 다시 돌아오도록 혼동을 덜게 하였다.

       

       




       학회원분의 코드 리뷰 

      혜민님의 4주차 스터디 코드를 통해 코드 리뷰를 진행해보았다.

       

      더보기
      #include <iostream>
      using namespace std;
      
      class f_circle { // 평면 원 클래스, flat circle
      private:
          int radius; // 반지름
      
      public:
          //기본 생성자
          f_circle() {}
          // 생성자
          f_circle(int r) {
              radius = r;
              cout << "***평면 원 계산기 접속***" << endl << endl;
          }
      
          // 소멸자
          ~f_circle() {
              cout << "평면 원 계산기 종료..." << endl << endl;
          }
      
          // 넓이
          double area() {
              return radius * radius * 3.14;
          }
      
          // 둘레
          double round() {
              return radius * 2 * 3.14;
      
          }
      };
      
      class cylinder {
      private:
          int height; // 원기둥 높이
          int radius; // 원기둥 반지름
      public:
      
          //생성자
          cylinder(int h, int r) {
              height = h;
              radius = r;
              cout << "***원기둥 계산기 접속***" << endl << endl; 
          }
          //소멸자
          ~cylinder() {
              cout << "원기둥 계산기 종료..." << endl << endl;
      
          }
      
          //겉넓이
          double surface() {
              return 2 * 3.14 * radius * height + 2 * 3.14 * radius * radius; // 원기둥 겉넓이 계산
      
          }
          //부피
          double volume() {
              return 3.14 * radius * radius * height; // 원기둥 부피 계산
      
          }
      };
      class cone {
      private:
          int height; // 원뿔 높이
          int radius; // 원뿔 반지름
      public:
      
          //생성자
          cone(int h, int r) {
              height = h;
              radius = r;
              cout << "***원뿔 부피 계산기 접속***" << endl << endl;
      
          }
          //소멸자
          ~cone() {
              cout << "원뿔 부피 계산기 종료..." << endl << endl;
      
          }
          //부피
          double volume() {
              return 3.14 * radius * radius * height * 1 / 3; // 원뿔 부피 계산
      
          }
      };
      class sphere {
      private:
          int radius;
      public:
      
          //생성자
          sphere(int r) {
              radius = r;
              cout << "***구 계산기 접속***" << endl << endl;
      
          }
          // 소멸자
          ~sphere() {
              cout << "구 계산기 종료..." << endl << endl;
      
          }
          //겉넓이
          double surface() {
              return 4 * 3.14 * radius * radius;
      
          }
          //부피
          double volume() {
              return 4 / 3 * 3.14 * radius * radius * radius;
      
          }
      };
      int main() {
      
          int n;
          
          while (1) {
      
              int radius;
              int height;
              cout << "원하는 계산을 선택하세요" << endl;
              cout << "1. 원의 넓이 계산" << endl;
              cout << "2. 원의 둘레 계산" << endl;
              cout << "3. 원기둥의 부피 계산" << endl;
              cout << "4. 원기둥의 겉넓이 계산" << endl;
              cout << "5. 원뿔의 부피 계산" << endl;
              cout << "6. 구의 부피 계산" << endl;
              cout << "7. 구의 겉넓이 계산" << endl;
              cout << "8. 종료" << endl;
              cout << "선택:";
              cin >> n;
              cout << endl;
      
              switch (n) {
                  
              case 1: //평면 원 넓이
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  {
                      f_circle c1(radius); // c1 객체 생성, 생성자 호출 , 반지름 radius를 넣어 초기화
                      cout << "평면원의 넓이:" << c1.area() << endl << endl;
                  } // 이거 중괄호로 안 감싸면 '초기화가 생략되었다'는 오류가 뜬다. 
                  break;
              case 2://원의 둘레
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  {
                      f_circle c2(radius); // c2 객체 생성, 생성자 호출 , 반지름 radius를 넣어 초기화
                      cout << "평면원의 둘레:" << c2.round() << endl << endl;
                  }
                  break;
              case 3://원기둥 부피
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  cout << "높이 입력:";
                  cin >> height;
                  cout << endl;
                  {
                      cylinder c3(height, radius); //c3 객체 생성, 생성자 호출, 높이 height, 반지름 radius를 넣어 초기화
                      cout << "원기둥의 부피:" << c3.volume() << endl << endl;
                  }
                  break;
              case 4://원기둥 겉넓이
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  cout << "높이 입력:";
                  cin >> height;
                  cout << endl;
                  {
                      cylinder c4(height, radius); //c4 객체 생성, 생성자 호출, 높이 height, 반지름 radius를 넣어 초기화
                      cout << "원기둥의 겉넓이:" << c4.surface() << endl << endl;
                  }
                  break;
              case 5://원뿔 부피
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  cout << "높이 입력:";
                  cin >> height;
                  {
                      cone c5(height, radius);
                      cout << "원뿔의 부피:" << c5.volume() << endl << endl;
                  }
                  break;
              case 6://구의 부피
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  {
                      sphere c6(radius);
                      cout << "구의 부피:" << c6.volume() << endl << endl;
                  }
                  break;
              case 7://구의 겉넓이
                  cout << "반지름 입력:";
                  cin >> radius;
                  cout << endl;
                  {
                      sphere c7(radius);
                      cout << "구의 겉넓이:" << c7.surface() << endl << endl;
                  }
                  break;
              case 8:
                  cout << "프로그램을 종료합니다" << endl;
                  return 0; // 8입력하면 바로 위에 문구 띄우고 프로그램 종료
              default: // 1~8 이외에 숫자를 입력했을 때 
                  cout << "잘못된 숫자입니다." << endl << endl; 
      
      
              }
          }
      }

       

       


       

       

       

      원 클래스

       

      원, 원기둥, 원뿔, 구의 4개의 클래스를 분류해서 코드를 간결하게 작성해주셨다!!

      각각의 클래스에 필요에 맞는 계산들을 수행해주신 것이 나타나있다.

       

      생성자에 원뿔 부피 계산기 접속, 소멸자에 원뿔 부피 계산기 종료... 문구를 출력하도록 하여 시작과 끝에 적절한 문구가 수행될 수 있도록 코드를 잘 작성해주셨다. 또한 내가 했던 4주차 코드와 함께 다른 점들을 확인해보았는데 pie를 숫자로 적어주신 부분과 변수를 더욱 직관적으로 적어주신 부분이 보기 쉬워 잘 와닿았다.

       

       

      main()

       

      다음으로는 main함수 부분을 알아보았다.

      while문을 통해 메뉴가 계속 반복되는 것을 확인할 수 있었다.

      (처음 알게된 사실인데 사실 while(true)와 while(1)은 같은 것이였다..!!

      모두 break문으로 빠져나가기 전까지 내용을 반복하는 반복문이라는 점이 같다.)

       

      이 부분은 입력받은 n에 따라 원 계산을 진행해줄 switch문이다.

      주석을 필요할 때에만 잘 넣어주셔서 코드를 한눈에 파악하기 쉬웠다.

      (또한 나의 코드와 함께 확인해보았을 때 나는 각 case문마다 전체를 중괄호로 감싸는 식으로 코드를 짰었는데

      오류가 많이 나타나 애를 먹어서, 혹시 이 부분과 관련이 되어있지 않나 싶다.)

       

      main()함수에서 변수 radius, height를 초기화해주고 case문에서는 c1, c2, c3 ... c7까지의 객체를 생성해주셨다.

      그리고 클래스 함수에 초기화된 반지름, 높이는 각각의 원 계산을 수행하여 반영되었다.

       

      8을 눌렀을 때 "프로그램을 종료합니다" 문구와, 원 계산 메뉴에 없는 숫자를 입력하였을 때 잘못된 숫자를 출력하며 프로그램은 마무리된다.

       

       

      <코드 리뷰 및 개선점>
      이해가 쉬운 주석 설명, 클래스와 변수 이름, 올바른 코드 배치로 간결한 코드를 확인할 수 있었다.

      1.필요에 따라 "원하는 계산을 선택하세요" 메뉴 부분 문구를 다른 클래스로 생성해 main()에 배치하고, main()에 switch문만 남겨두는 형태로 진행하여도 좋을 것 같다는 생각이 들었다.
      2. 3.14로 표기하는 대신 cmath헤더를 포함해 M_PI를 사용해주는 방법도 이용할 수 있다.
      3. switch문을 하나의 calculate클래스 함수로 분리한 뒤, main함수 내에 있던 중복이 되는 코드나 계산을 최대한 중복하지 않고 사용할 수 있는 방안을 생각해볼 수 있다.

       

      댓글