c++ 관련하여 질문을 해도 되나요..?

익명 사용자의 이미지

//-------------------------------------------------------------------------
// ShapeManager.cpp			도형을 관리하는 클래스
//	
// 2018. 11. 26	by Wulong
//-------------------------------------------------------------------------
#include <iostream>
#include "ShapeManager.h"
#include "Triangle.h"
#include "Circle.h"
#include "Rectangle.h"
#include "Line.h"
using namespace std;
 
//------------------------------------------------------
ShapeManager::ShapeManager(int n)
//------------------------------------------------------
{
	nShape = 0;												// 처음 만들어질 때는 관리하는 도형 수가 0임
	capacity = n;											// 최대 n개의 도형을 담을 수 있음
	shapes = new Shape*[capacity];
}
 
//------------------------------------------------------
ShapeManager::~ShapeManager()
//------------------------------------------------------
{
	// 모든 객체가 정확하게 삭제되는지 반드시 확인하여야 한다.
	delete[] shapes;										// 도형관리자가 관리하는 도형의 소멸자를 호출함
};
 
//------------------------------------------------------
void ShapeManager::insert(Shape* a)
//------------------------------------------------------
{
	if (nShape >= capacity) {
		cout << "최대 갯수를 초과했습니다. 더 많은 저장소를 만들겠습니다." << endl;
		capacity += 100;
		newshapes = new Shape*[capacity];
		memcpy(newshapes, shapes, sizeof(shapes));
		for (int i = 0; i < nShape; ++i)
			delete shapes[i];
		shapes = newshapes;
 
	}
	shapes[nShape] = a;
	nShape++;
};
 
//------------------------------------------------------
void ShapeManager::Delete(int n)
//------------------------------------------------------
{
	delete shapes[n];
	for (int i = n; i < nShape; ++i) {
		shapes[i] = shapes[i + 1];
	}
	--nShape;
}
 
//------------------------------------------------------
void ShapeManager::ClassDelete(int num)
//------------------------------------------------------
{
	switch (num) {
	case 1:
		for (int i = 0; i < nShape; ++i) {
			if (dynamic_cast<Triangle*>(shapes[i]) != nullptr) {
				Delete(i);
				i--;
			}
		}
		cout << "삼각형 제거가 완료되었습니다. " << endl;
		break;
	case 2:
		for (int i = 0; i < nShape; ++i) {
			if (dynamic_cast<Rectangle*>(shapes[i]) != nullptr) {
				Delete(i);
				i--;
			}
		}
		cout << "사각형 제거가 완료되었습니다. " << endl;
		break;
	case 3:
		for (int i = 0; i < nShape; ++i) {
			if (dynamic_cast<Circle*>(shapes[i]) != nullptr) {
				Delete(i);
				i--;
			}
		}
		cout << "원 제거가 완료되었습니다. " << endl;
		break;
	case 4:
		for (int i = 0; i < nShape; ++i) {
			if (dynamic_cast<Line*>(shapes[i]) != nullptr) {
				Delete(i);
				i--;
			}
		}
		cout << "선 제거가 완료되었습니다. " << endl;
		break;
	}
}
 
void ShapeManager::Menu()
{
	cout << "추가적인 명령을 수행할 수 있습니다." << endl << "Menu" << endl << endl;
	cout << "1 - 원하는 도형 추가" << endl << "2 - 전체 도형을 그리기" << endl << "3 - 도형을 제거하기" << endl << "4 - 프로그램 끝내기" << endl;
	cout << "번호로 입력하세요 : ";
 
}
 
 
//------------------------------------------------------
void ShapeManager::draw() const
//------------------------------------------------------
{
	cout << "------------------------------------------------------------------" << endl;
	cout << "관리하는 모든 도형을 그립니다." << endl;
	cout << "최대 " << capacity << "개의 도형을 담을 수 있습니다." << endl;
	cout << "모두" << nShape << "개의 도형이 있습니다." << endl;
	cout << "------------------------------------------------------------------" << endl << endl;
	for (int i = 0; i < nShape; ++i) {
		cout << "[" << i << "]  ";
		shapes[i]->draw();
	}
	cout << endl;
 
 
 
	cout << "------------------------------------------------------------------" << endl;
	cout << "그리기를 마칩니다." << endl;
	cout << "------------------------------------------------------------------" << endl << endl;
};

이런 프로그램을 하고 있습니다. 도형을 추가하는 부분에서 처음 주어진 공간보다 더 많은 양을 만들려고 할 경우 새 공간을 만들어 원래 있던 공간의 데이터를 복사하고, 원래 있던 공간을 삭제하고 하는 식으로 해결하려고 했는데, 잘 되지 않습니다. 어떤 식으로 풀어나가야 할지 힌트나 도움을 좀 주실 수 있나요?

익명 사용자의 이미지

네, 물론 할 수 있습니다.

코드 대충 읽고 발견한 문제들을 심각한 것에서 덜 심각한 것 순서로 말씀드리지요. 뒤쪽 순서로 갈수록 문제는 덜 심각해지며 기능상의 문제라기보단 성능이나 프로그래밍 스타일 차원의 문제에 가까워지므로 도저히 신경쓸 수 없다면 무시하셔도 됩니다. 그래도 전부 한 번씩 생각해 보시는 게 좋습니다.

(1) insert는 원본을 깊게 복사(deep copy)하지 않은 채 원본을 삭제(delete)하고 있습니다. 이러면 사본인 newshapes는 대상이 삭제된 포인터들을 갖고 있게 됩니다.

(2) insert 내부의 memcpy에 들어가는 크기 매개변수 sizeof(shapes)는 귀하께서 의도하신 값이 아닐 겁니다. shaped의 타입을 알려주지 않으셔서 확신할 수는 없지만 기껏해야 포인터 변수 하나 크기일 겁니다. 동적 배열 전체를 복사하고 싶으신 거라면, 런타임에 그 정보는 nShape이나 capacity가 알고 있을 거라는 사실을 고려해야 합니다.

(3)~ShapeManager는 동적 배열 shaped를 삭제하지만, 동적 배열에 담긴 포인터가 가리키는 Shape객체를 삭제하진 않습니다. 그 결과 메모리가 줄줄 새게 될 겁니다.

(4) ClassDelete 함수는 삭제할 대상이 두 개 이상일 경우 끔찍하게 비효율적입니다. 소위 "러시안 페인트공 알고리즘"의 변종인데, 이 변종에 따로 이름이 있는지는 모르겠지만 제법 흔히 저질러지는 실수인 것 같습니다.

효율적이지 않은 알고리즘을 짠다고 당장 뭔가 심각하게 잘못되는 것은 아니지만, 같은 기능을 하는 더 효율적인 알고리즘을 그다지 어렵지 않게 작성할 수 있다는 사실을 한번 확인해 보세요. 좋은 훈련이 될 겁니다. 귀하께서 추후 어떤 프로그래머가 되든 이 정도 문제는 해결할 수 있어야 합니다.

(5) ClassDelete 내부의 코드는 소위 "폭포식(cascading) dynamic_cast"이라고 해서, C++에서 그다지 권장되지 않는 스타일의 코드입니다. 특히 dynamic_cast는 성능상 불이익이 크며, 여기서는 그 기능을 고작 삭제 대상을 판별하기 위해서 쓰고 있다는 점이 주목할 만하군요.

최소한 제 상식에 따르면 가상 함수가 무엇인지 배우지 않은 상태에서 dynamic_cast를 배우고 사용한다는 건 납득하기 어려운 일입니다. 해당 코드를 가상 함수를 이용하는 메커니즘으로 다시 구현해 보세요. 좀 더 C++답고 성능도 좋은 코드가 나올 겁니다.

번외. (1)과 (2)는 직접 구현한 자료구조 대신 STL 시퀀스 컨테이너를 사용함으로써, (3)은 C++11 이후 추가된 스마트 포인터를 사용함으로써 간편하게 해결이 가능합니다. (4) 역시 C++17 이후 추가된 STL algorithm remove_if를 이용해서 더 좋은 성능으로 훨씬 쉽게 구현이 가능합니다. 당장 STL을 배우기 어렵거나 STL 사용이 금지되었다면 어쩔 수 없겠지만, 그렇지 않다면 한 번 알아보시길.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.