[C++] 객체의 종류와 상관없이 delete

awdxawdx101의 이미지

어떤 객체를 인자로 받든, 상관없이 delete 해주고 싶습니다.

객체도 포인터의 일종이고, 종류와 상관없이 받고싶어서 void* 형을 생각했습니다.

..
void releaseObj(void * obj){
    delete obj;
}
...
 
Car car;
Bike bike;
relaseObj(bike);
relaseObj(car);

그래서 위와 같은 코드를 만들었는데 에러가 나더군요. void로 바뀌면서 소멸자를 못찾는건가요?

아니면 위 코드 말고 다른 방법 있을까요?

익명 사용자의 이미지

어떻게든 비슷하게 그런 걸 "흉내" 내 볼 수는 있겠지만, 대체 왜요?

이런 코드를 짜고 싶다면 java를 쓰는 게 나을 듯.

awdxawdx101의 이미지

옵션에 따라 따로 delete하는게 아닌, releaseObj()를 호출해서 일관성있게(?) 자원을 릴리즈 해주고싶었습니다.

Stephen Kyoungwon Kim@Google의 이미지

C++은 컴파일할 때, 객체의 내용이 아니라 타입을 보고 일을 처리합니다. void*로 object가 포인팅이 되어 있으면 object를 destroy할 때 destructor를 부르는데, 뭘 부를지 알 방법이 없죠.

그리고 질문이 이해가 안 됩니다. delete obj 자체가 obj의 타입이 뭐든 destruction을 수행해 주죠. 안 되는 경우라면 obj가 pointer이고 메모리 할당할 때 하나가 아니라 연속된 n개를 해서 delete[]를 써줘야 하는 경 우 뿐인 것 같습니다. 왜 여기에 굳이 wrapper가, 단지 타입 상관없이 call을 해주기 위해 필요하죠?

C++에서 "타입과 관계없이" 어떤 일을 하는 건 두 가지가 있습니다. 첫째, 사실 타입마다 그 어떤 일을 하는 코드를 따로 써주게 되긴 하지만 그 작업을 컴파일러에게 떠넘기고 사람은 한 개의, 타입과 무관하게 적용되는 코드를 써주는 것입니다. 템플릿이라고 부릅니다. 아래 첫 코드 박스가 템플릿입니다.

두번째는 JAVA처럼 inheritance와 virtual member function을 활용하는 것인데, JAVA와는 달리 모든 객체가 상속하는 Object 같은 객체가 있지 않아서 과거와 미래에 생성되었거나 생성될 모든 객체에 적용하기는 어렵겠죠.

위의 releaseObj를 예로 들면, 이렇겠죠.

template<typename T>
void releaseObj(T* obj) {
  if (obj)  delete obj;
  return;
}

이를테면, foo.cc라는 파일에 foo라 는함수가 있고 다음처럼 되어 있다고 해보죠.

void foo() {
 int *p = new int(400);
 std::string *s = new std::string("hello");
 // some more codes here
 releaseObj(p);
 releaseObj(s);
}

컴파일러는 동일한 releaseObj를 p와 s에 대해 사용하지 않습니다. (Python은 그렇게 하죠)

대신 위의 releaseObj의 템플릿을 보고 아래 코드를 직접 만듭니다.

void releaseObj(int* obj) {
  if (obj)  delete obj;
  return;
}
void releaseObj(std::string* obj) {
  if (obj)  delete obj;
  return;
}

템플릿이 컴파일러에 의해 어떻게 코드 생성에 활용되고 실제 코드가 어떻게 생성되는지, 그 디테일은 위의 제 설명과는 좀 다릅니다. 그렇지만 개념 자체는 비슷하다고 생각하셔도 됩니다.

보시다시피 C++에서는 정말로 여러 개의 다른 타입의 object에 동일하게 적용되는 연산은 inheritance와 virtual member function을 활용하는 것밖에 없습니다. 그렇지만 동일한 piece of code를 타입 무관하게 적용하는 방식은 있는데, 이건 거칠게 말해 그 piece of code가 타입에 무관하게 동작하는 게 아니라 컴파일러나 preprocessor가 프로그래머가 적어놓은 piece of code (매크로, 템플릿 등)를 이용해서 타입 별로 별개의 코드를 일일이 자동 생성하는 쪽에 가깝습니다.

그래서 안 되는 게 어떤 거냐, 이런 겁니다. Java는 이게 되죠.

LinkedList<Object> object = new LinkedList<Object>();
object.add ( new String("hello") );
object.add( new Integer(40) ); 
for (Object e : object)
  System.out.println(o.toString());

C++은 저게 안 됩니다. 되기야 할 수도 있겠지만 아마 상당히 복잡한 코드를 기대해야 할 겁니다. 회사에서는 그 정도 복잡한 코드는 아마 관리하기 어렵기 때문에 못 쓰게 할 겁니다.

이렇게 타입과 무관하게 무슨 공통된 일을 해주는 건 인터프리팅이 컴파일보다 유리합니다. 대신 후자가 일반적으로 성능이 낫죠. C++은 둘다 잡으려고 했기 때문에 희생한 게 있는데, 템플릿, 매크로, 메타 프로그래밍 등을 더 깊이 쓰면 쓸수록 코드가 복잡해지거나 관리하고 디버깅하기 복잡해집니다.

awdxawdx101의 이미지

답변 감사합니다

jick의 이미지

윗분 말씀에 동의합니다.

C++은 기본적으로 "이 줄을 수행하는 순간 코드와 개발자는 이 변수의 타입이 뭔지 알고 있다"라고 가정합니다. 물론 C++이 오만가지 변태적인 기능을 다 제공하는 언어라 어느 정도 벗어날 수는 있지만, 벗어나면 벗어날수록 코드가 지저분해지고 유지보수가 불가능한 코드가 됩니다.

정 "임의의 타입을 받는 변수"를 원하시면 template를 쓸 수는 있는데, 딱히 초보자에게 권장하고 싶지는 않습니다. (C++을 웬만큼 한다는 개발자들도 일단 template의 주화입마에 빠지게 되면 다음 사람이 보고 각혈하는 스파게티 괴물을 만들어놓는 꼴을 너무 많이 봐서...)

* 그나저나, 문제에서 Car, Bike가 클래스의 이름이라면 스택에 생성되는 변수니까 delete를 할 필요가 없을 텐데요...;;

awdxawdx101의 이미지

제가 아직 덜배워서 그걸 몰랐네요.

new로 동적할당 시 delete를 쓴다는걸 방금 알았습니다

익명 사용자의 이미지

사실 어떻게든 하려고 마음만 먹는다면, 순수 가상 소멸자를 정의하는 Object class를 C++에서 정의한 뒤 (내가 만든) 모든 클래스가 Object를 상속받게 만들면 되기는 합니다.

그러고 나서 Object에 대한 스마트 포인터를 이용해 코드를 작성하는 거죠.

다만...

1. 내가 안 만든 클래스(대표적으로 C++ 표준 라이브러리 클래스) 및 클래스가 아닌 타입들(int, bool, etc.)은 Object으로 다룰 수 없습니다.

2. 가상 함수 사용에 따른 cost가 추가되지요. (최소 객체당 포인터 한 개 크기씩은 늘어날겁니다. 크기는 작은데 많이 만들어야 하는 클래스에게는 타격이 크겠지요.)

3. 위에서도 여쭤봤지만, 이런 코드를 왜 짜려고 하나요?

C++에서 받은 객체의 타입을 어느 정도 범위 안에서 확신할 수 있게 해 준다는 건, 이견이 있으실 수야 있겠습니다만 저는 장점에 가깝다고 생각합니다.

파이썬을 비롯한 몇몇 언어에서는 함수를 호출할 때 이것저것 다양한 타입의 객체들을 컨테이너 안에서 막 뒤섞어서 전달해 줄 수 있지요. 잘 돌 때는 편리합니다. 제대로 안 돌 때는 꼭 뒤늦게 문제가 터지기 때문에 문제의 근원이 어디에 있는지 찾기가 꽤 번거롭습니다.

C++에서도 템플릿을 쓰거나 위와 같이 광범위한 base class를 만들어서 그걸 흉내낼 수는 있겠습니다만.. 별로 추천하고 싶지 않습니다. 궁극적으로 하고 싶은 게 무엇이든간에 좀 더 C++다운 코드로 나타내실 수 있을 겁니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.