템플릿 구현 시 value type의 리스트를 만들었을 때 소멸자 문제

dragonHu의 이미지

안녕하세요 c++을 공부하고 있는 학생입니다.
list를 구현할때 소멸자에 대하여 궁금한점이 있어 질문드립니다.

template <typename T>
class ChainNode
{
private:
  T data;
  ChainNode<T> * nextNode;
  friend class Chain<T>;
};
 
template <typename T>
class Chain
{
private:
  ChainNode<T> * front;
public:
  class ChainIterator
  {
 
  };
};

이러한 구조의 리스트를 만들려하는데요
Chain객체를 소멸하면 소멸자를 통해서 모든 노드들을 삭제하려합니다.
  ~ChainNode<T>()
   {
     delete data;
     delete nextNode;
   }

생각한 방법은 위와 같이 연쇄적으로 할 예정이였는데요(예외처리는 아직 염두에 두지 않았어요)

그런데 Chain<int> 이런식으로 포인터 타입이 아닌 value 타입으로 리스트를 선언하면
delete data; 를 할때 참조 수준이 달라서 컴파일 오류가 발생하잖아요

즉 T가 포인터 타입(data가 포인터 변수)이라면 ChainNode 의 멤버인 data 까지 해제하고
value 타입이라면 ChainNode만 해제하는 코드를 작성하고 싶은데
이 문제를 어떻게 해결해야 하는지 잘 모르겠습니다.
std list 헤더파일을 살펴는 봤는데 제가 읽기엔 너무 어려운 코드였습니다..
꼭 답변부탁드립니다 ㅠ

 의 이미지

멤버변수 int data에 대해서 delete data;가 가능할 거라고 생각하시는 이유가...?

delete는 주소에 대해, 특히 new를 이용하여 동적 할당된 메모리 주소에 대해서 사용하는 것입니다.
일반 멤버 변수는 클래스 소멸 시 자동으로 같이 소멸하므로 소멸자에 따로 명시해 줄 필요는 전혀 없습니다.

dragonHu의 이미지

질문을 이상하게 했네요 죄송합니다.

 의 이미지

다시 읽었습니다.

그러니까, Tint일 때는 별다른 처리를 하지 않지만, Tint *할 때는 delete하도록 Class ChainNode의 소멸자를 짜겠다는 거죠?

짧게 말씀드리지요. 좋은 생각이 아닙니다.

애초에 int *delete해도 안전하다는 보장을 누가 해 주는데요?
오직 new로 할당된 주소만이 delete로 해제할 수 있습니다.
하지만 그렇지 않은 경우가 있죠. 그것도 엄청 다양하게!

1) 지역 변수, 전역 변수 혹은 정적 변수의 주소
당연히 delete를 포함한 어떤 방법으로도 해제 안 됩니다.

2) malloc, calloc 혹은 realloc으로 할당된 메모리 주소
free로 해제해야죠.

3) 동적 할당된 구조체, 공용체, 클래스 등의 멤버 주소
당연히 원래 객체를 통째로 해제해 줘야 합니다.

4) new int[]으로 할당한 배열의 한 요소
원래의 배열을 delete []으로 해제해 줘야 합니다

5) 기타. alloca로 할당받았던가, memory-mapped file이 매핑된 주소라던가 등 플랫폼에서 제공하는 온갖 종류의 메모리 공간 할당 방법들!
제각각 자기만의 해제 방법이 따로 있답니다.

보통 int * 하나 툭 던져주면 이게 해제할 수 있는 건지, 어떻게 해제해야 하는 건지 알아낼 표준적인 방법은 전혀 없습니다. 좀 일반적으로 말해서, T *라고 해도 마찬가집니다. 실제로 std::listT * 따위를 던져서 instantiation 하면 원소가 삭제될 때 포인터가 가리키는 메모리를 삭제하려는 시도 따위 전혀 하지 않습니다. 애초에 할 수 있는 방법이 없으니까요. 그래서 STL container에 동적 할당된 포인터들을 채워 놓고 remove-erase 구문 따위를 돌리면 메모리가 줄줄 새지요.

소멸자에서 포인터 필드에 delete를 쓰려면, 그 포인터가 반드시 new로 할당되었음을 확신할 수 있어야 합니다. 대개 private 포인터이고 자신이 직접 new로 할당되도록 작성했을 경우에는 그럴 수 있죠. 포인터가 외부에서 날라왔을 경우에는 그런 확신을 가지기 어렵습니다. 사용자 매뉴얼에 "꼭 new로 할당된 포인터만 전달해주세요"라고 써놓고 사용자의 지적 능력을 믿으시던가요.

포인터가 소멸할 때 알아서 할당이 해제되도록 하고 싶으신가요? C++11에서 추가된 스마트 포인터들을 쓰세요.

http://en.cppreference.com/w/cpp/memory/unique_ptr
http://en.cppreference.com/w/cpp/memory/shared_ptr

어떻게든 포인터 타입과 비포인터 타입에 대해 다르게 동작하는 템플릿 코드를 작성하고 싶으신가요?
그런 분들을 위해서 제가 예제 코드를 준비해 오긴 했습니다.

C++11에서 추가된 is_pointer를 쓰면 간단해지는데, 간혹 C++11을 사용할 수 없는 경우도 있을 수 있죠. 그래도 어떻게든 C++11 템플릿 라이브러리의 일부를 가져와서 구현할 수는 있었습니다. http://en.cppreference.com/ 에서 Possible implementation 좀 긁어왔습니다.

#include <iostream>
 
#if __cplusplus >= 201103L
#include <type_traits>
#else
// en.cppreference.com/w/cpp/types/remove_cv
template< class T > struct remove_const          { typedef T type; };
template< class T > struct remove_const<const T> { typedef T type; };
 
template< class T > struct remove_volatile             { typedef T type; };
template< class T > struct remove_volatile<volatile T> { typedef T type; };
 
template< class T >
struct remove_cv {
    typedef typename remove_volatile<typename remove_const<T>::type>::type type;
};
 
template< bool v > struct bool_constant { enum {value = v}; };
struct true_type  : public bool_constant<true> {};
struct false_type : public bool_constant<false> {};
 
// en.cppreference.com/w/cpp/types/is_pointer
template< class T > struct is_pointer_helper     : public false_type {};
template< class T > struct is_pointer_helper<T*> : public true_type {};
template< class T > struct is_pointer : public is_pointer_helper<typename remove_cv<T>::type> {};
#endif
 
using namespace std;
 
template< typename T, bool is_pointer >
class sample_base;
 
template< typename T >
class sample_base<T, false>{
protected:
	T field;
public:
	sample_base(T init):field(init){
		return;
	}
	~sample_base(void){
		cout << "This object has no pointer." << endl;
		return;
	}
};
 
template< typename T >
class sample_base<T, true>{
protected:
	T field;
public:
	sample_base(T init):field(init){
		return;
	}
	~sample_base(void){
		cout << "This object has a pointer." << endl;
		delete field;
		return;
	}
};
 
template< typename T >
class sample : protected sample_base<T, is_pointer<T>::value>{
public:
	typedef sample_base<T, is_pointer<T>::value> base;
	sample(T init):base(init){
		return;
	}
	~sample(void){
		return;
	}
 
	// other member functions...
};
 
int main(void){
#ifdef __cplusplus
	cout << "__cplusplus: " << __cplusplus << endl;
#else
	cout << "no __cplusplus defined." << endl;
#endif
	{
		cout << "sample<int>:" << endl;
		sample<int> sample_int(0);
	}
	{
		cout << "sample<int *>:" << endl;
		sample<int *> sample_int_p(new int(0));
	}
	return 0;
}

중요하니까 다시 한 번. T *가 주어졌다고 항상 delete가 가능한 건 아닙니다. 위 코드는 포인터와 비포인터 타입 인수에 따라 다른 코드를 실행시키는 예시일 뿐, 절대 그대로 실용적인 목적으로 사용되어서는 안 됩니다.

댓글 달기

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