c++ 질문입니다.

ozon1000의 이미지

#include<iostream>
#include<cstdlib>
 
using namespace std;
 
class Array1D
{
private:
	int* arr1;
	int len1;
public:
	Array1D(int n = 0) : len1(n)
	{
		arr1 = new int[n];
	}
	int& operator[](int index)
	{
		if (index < 0 || index >= len1)
		{
			cout << "Out of index range" << endl;
			exit(0);
		}
		return arr1<ol>
</ol>
;
	}
	int Getlen()
	{
		return len1;
	}
	~Array1D()
	{
		cout << "Array1" << endl;
		delete[]arr1;
	}
};
 
class Array2D
{
private:
	Array1D* arr2;
	int len;
public:
	Array2D(int n1, int n2) :len(n1)
	{
		arr2 = new Array1D[n1];
		for (int i = 0; i < n1; i++)
			arr2[i] = Array1D(n2);
	}
	Array1D& operator[](int index)
	{
		if (index < 0 || index >= len)
		{
			cout << "Out of index range" << endl;
			exit(0);
		}
		return arr2<ol>
</ol>
;
	}
	int Getlen()
	{
		return len;
	}
	~Array2D()
	{
		cout << "Array2" << endl;
		delete[]arr2;
	}
};
 
int main()
{
	Array2D arr(2, 2);
 
	for (int k = 0; k < 2; k++)
	{
		for (int p = 0; p < 2; p++)
			arr[k][p] = 1;
	}
	cout << arr[1][1] << endl;
	return 0;
}

위에 있는 저 코드는 솔직이 무슨것을 하고자 하는지 혹은 어떤 구조로 정확히 돌아가는지 잘 모르겠습니다.
혹 도움을 주실분이 있으신지요..

shint의 이미지

- 각 주소를 printf("%x", arr1)로 찍어봅니다.
- 생성자 소멸자에 출력순서를 확인해봅니다.

확인해보니. 생성 소멸 순서가. 좀 이상하네요.
생성자는 리턴값이 없는데. = 을 사용한것도 좀 걸립니다.
내용만으로 보면. 배열을 하나씩 생성해서 2차원 배열을 사용하려는 의도로 보입니다.

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

익명 사용자의 이미지

어설프게 작성되긴 했지만, [] 연산자 오버로딩을 이용하여 2차원 배열을 흉내내는 예제로군요.
포인트는 main 함수의 Array2D arr(2,2)가 마치 int arr[2][2];처럼 쓰이고 있다는 겁니다.

1. Array1D와 Array2D의 내부 코드는 거의 같습니다. 타입이 다를 뿐이죠. Array1D는 int의, Array2D는 Array1D의 동적 할당된 배열을 가지고 있습니다.
2. array[k][d]를 보세요. 먼저 array[k]Array2D::operator[] 함수를 부르고, 이 함수는 객체 내부의 배열에서 인덱스에 맞는 객체를 찾아 Array1D&타입으로 반환합니다. 그 다음 [d]가 달라붙어 Array1D::operator[]가 불리고, 마찬가지의 과정으로 적절한 객체가 int& 타입으로 반환됩니다.
3. 두 operator[] 함수들이 인덱스 범위 체크를 하고 있다는 점을 주목해보세요. C/C++의 raw array는 범위 체크를 하지 않죠.
4. 소멸자에서 동적 할당된 배열을 삭제하고 있다는 점도 눈여겨볼 만 합니다.

ozon1000의 이미지

답변 감사합니다. 분석결과 Arry1D 임시객체가 생성되고 직후에 바로 소멸되기 때문에

결국 같은 heap공간을 두번 메모리 해제하는 오류가 있네요.
원인은 생성자는 return되는 값이 없는데 arr2[i] = Array1D(n2); 여기에서 문제가 있는 듯 합니다.
그렇다면 operator=를 새로 정의 해주어야 한다는 것인데...
혹여 오류가 나지 않는 이 코딩에서 벗어나지 않는한에서 해결 된 코딩을 제가 받아볼 수 있을까요?

익명 사용자의 이미지

#include <iostream>
#include <cstdlib>
 
using namespace std;
 
class Array1D
{
private:
	int* arr1;
	int len1;
public:
	Array1D(int n = 0) : len1(n)
	{
		arr1 = new int[n];
	}
	// "깊은 복사"를 수행하도록 합시다.
	void deep_copy(const Array1D &orig) {
		len1 = orig.len1;	// 길이 정보를 가져오고
		arr1 = new int[len1];	// 메모리를 새로 할당한 뒤
		for (size_t i = 0;i < len1;i++) {
			arr1[i] = orig.arr1[i];	// 루프를 돌면서 데이터를 모두 복사합니다.
		}
	}
	// 깊은 복사를 하는 대입 연산자가 필요합니다.
	Array1D &operator=(const Array1D &rhs) {
		if (this == &rhs)	// 자기 자신을 대입하는 경우
			return *this;	// 즉시 반환합니다.
 
		delete[] arr1;	// 이전에 할당되었던 메모리는 해제합니다.
		deep_copy(rhs);
		return *this;
	}
	// 하는 김에, 깊은 복사를 하는 복사 생성자도 만들어 봅시다.
	Array1D(const Array1D &orig) :arr1(NULL) {
		deep_copy(orig);
	}
	int& operator[](int index)
	{
		if (index < 0 || index >= len1)
		{
			cout << "Out of index range" << endl;
			exit(0);
		}
		return arr1<ol>
</ol>
;
	}
	int Getlen()
	{
		return len1;
	}
	~Array1D()
	{
		cout << "Array1" << endl;
		delete[]arr1;
	}
};
 
class Array2D
{
private:
	Array1D* arr2;
	int len;
public:
	Array2D(int n1, int n2) :len(n1)
	{
		arr2 = new Array1D[n1];
		for (int i = 0; i < n1; i++)
			arr2[i] = Array1D(n2);
			/*
			이 코드를 주목해 보세요.
			별도의 대입 연산자가 정의되지 않으면, 디폴트 대입 연산은 얕은 복사입니다.
			Array1D(n2)의 arr1 필드(동적 할당된 배열)가 arr2[i]에 그대로 대입되는 거죠.
			문제는 Array1D 임시 객체가 즉시 소멸하면서 할당되었던 int 배열이 즉시 소멸한다는 겁니다.
			그 결과 arr2[i]의 arr1 필드는 이미 해제된 배열 메모리를 쥐게 됩니다.
			당장은 큰 문제가 없을지 몰라도 분명한 문제 상황이고, 이는 특히 array[i] 객체가 삭제될 때
			MSVC의 디버그 모드가 동일한 메모리의 두 번째 delete를 잡아내어 에러 메시지를 띄우는 것입니다.
 
			이 문제를 해결하려면 위에서 제가 한 것처럼 대입 연산자가 깊은 복사를 하게 만들어야 합니다.
			따로 불거지지만 않았을 뿐 Array2D도 동일한 문제를 가지고 있는데, Array2D를 위한 대입 연산자
			( + 복사 생성자)를 직접 짜는 건 연습 문제로 남겨 드리겠습니다. :)
 
			덧. C++11의 기능을 이용하면 더 우아하게 해결할 수도 있습니다.
			move semantic, shared_ptr, array 컨테이너 등이 추가되었거든요.
			*/
	}
	Array1D& operator[](int index)
	{
		if (index < 0 || index >= len)
		{
			cout << "Out of index range" << endl;
			exit(0);
		}
		return arr2<ol>
</ol>
;
	}
	int Getlen()
	{
		return len;
	}
	~Array2D()
	{
		cout << "Array2" << endl;
		delete[]arr2;
	}
};
 
int main()
{
	Array2D arr(2, 2);
 
	for (int k = 0; k < 2; k++)
	{
		for (int p = 0; p < 2; p++)
			arr[k][p] = 1;
	}
	cout << arr[1][1] << endl;
	return 0;
}
익명 사용자의 이미지

1. deep_copy 메서드는 public일 필요가 없고, 사실 여러 가지 측면에서 볼 때 private인 편이 훨씬 더 낫습니다.
2. 복사 생성자에서 꼭 이전에 할당한 메모리를 해제할 필요는 없을지도 모릅니다.
- 같은 크기의 배열을 대입할 땐 전에 할당해 놓은 메모리를 재사용할 수 있죠. (직접 그렇게 하도록 짜보세요.)
- 더 생각해보면, 더 작은 크기의 배열이 대입될 때에도 마찬가집니다. 이 경우 "할당한 동적 배열의 원래 크기"를 나타내는 필드가 하나 더 필요해지겠죠. 이 정도까지 하려면 코드가 약간 더 많이 바뀌어야 합니다.
3. 생각해보니 꼭 C++11까지 가지 않더라도 vector라는 좋은 컨테이너가 있네요.

ozon1000의 이미지

c++의 STL vector의 작동방식을 흉내내고자 한 코드인것 같습니다.
성의있는 답변 감사드립니다.

shint의 이미지


2, 4로 출력 
 
arr2 new   3d3f2c
[1] arr1 new   3d3a68    0   len:4
[1] arr1 new   3d3a6c    1   len:4
[1] arr1 new   3d3a70    2   len:4
[1] arr1 new   3d3a74    3   len:4
operator=   this  :  3d3f2c
[deep_copy] arr1 new    3d3ab8
[deep_copy]   3d3ab8    0   len:4
[deep_copy]   3d3abc    1   len:4
[deep_copy]   3d3ac0    2   len:4
[deep_copy]   3d3ac4    3   len:4
Array1
&arr2[i] Array1D()   3d3f2c
[1] arr1 new   3d3a68    0   len:4
[1] arr1 new   3d3a6c    1   len:4
[1] arr1 new   3d3a70    2   len:4
[1] arr1 new   3d3a74    3   len:4
operator=   this  :  3d3f34
[deep_copy] arr1 new    3d3f68
[deep_copy]   3d3f68    0   len:4
[deep_copy]   3d3f6c    1   len:4
[deep_copy]   3d3f70    2   len:4
[deep_copy]   3d3f74    3   len:4
Array1
&arr2[i] Array1D()   3d3f34
main    3d3f2c  3d3ab8  1
main    3d3f2c  3d3abc  1
main    3d3f2c  3d3ac0  1
main    3d3f2c  3d3ac4  1
main    3d3f34  3d3f68  1
main    3d3f34  3d3f6c  1
main    3d3f34  3d3f70  1
main    3d3f34  3d3f74  1
1
Array2
Array1
Array1
 
--------------------------------
Process exited after 0.7167 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
 
4, 2 로 출력
 
 
arr2 new   3d3f2c
[1] arr1 new   3d3f98    0   len:2
[1] arr1 new   3d3f9c    1   len:2
operator=   this  :  3d3f2c
[deep_copy] arr1 new    3d3f58
[deep_copy]   3d3f58    0   len:2
[deep_copy]   3d3f5c    1   len:2
Array1
&arr2[i] Array1D()   3d3f2c
[1] arr1 new   3d3f98    0   len:2
[1] arr1 new   3d3f9c    1   len:2
operator=   this  :  3d3f34
[deep_copy] arr1 new    3d3f68
[deep_copy]   3d3f68    0   len:2
[deep_copy]   3d3f6c    1   len:2
Array1
&arr2[i] Array1D()   3d3f34
[1] arr1 new   3d3f98    0   len:2
[1] arr1 new   3d3f9c    1   len:2
operator=   this  :  3d3f3c
[deep_copy] arr1 new    3d3f78
[deep_copy]   3d3f78    0   len:2
[deep_copy]   3d3f7c    1   len:2
Array1
&arr2[i] Array1D()   3d3f3c
[1] arr1 new   3d3f98    0   len:2
[1] arr1 new   3d3f9c    1   len:2
operator=   this  :  3d3f44
[deep_copy] arr1 new    3d3f88
[deep_copy]   3d3f88    0   len:2
[deep_copy]   3d3f8c    1   len:2
Array1
&arr2[i] Array1D()   3d3f44
main    3d3f2c  3d3f58  1
main    3d3f2c  3d3f5c  1
main    3d3f34  3d3f68  1
main    3d3f34  3d3f6c  1
main    3d3f3c  3d3f78  1
main    3d3f3c  3d3f7c  1
main    3d3f44  3d3f88  1
main    3d3f44  3d3f8c  1
1
Array2
Array1
Array1
Array1
Array1
 
--------------------------------
Process exited after 0.9612 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . . 

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

shint의 이미지

#include <iostream>
 
#include<iostream>
#include<cstdlib>
 
using namespace std;
 
class Array1D
{
private:
	int* arr1;
	int len1;
public:
	Array1D(int n = 0) : len1(n)
	{
		arr1 = new int[n];
		printf("len1              %d\n", len1);
		printf("this              %x    ", this);
		printf("생성자1 arr1   :  %x\n", arr1);
	}
	int& operator[](int index)
	{
		printf("operator[1]   %x    %d   len:%d\n", &arr1[ index], index, len1);
		if (0 <= index && index < len1)
		{
			return arr1[ index];
		}
		else
		{
			cout < < "Out of index range" < < endl;
			exit(0);
		}
	}
	int Getlen()
	{
		return len1;
	}
	void Setlen(int n)
	{
		len1 = n;
		printf("len1              %d\n", len1);
	}
	~Array1D()
	{
		cout < < "소멸자1 Array1" < < endl;
		delete[]arr1;
	}
};
 
 
class Array2D
{
private:
	Array1D* arr2;
	int len;
public:
	Array2D(int n1, int n2) :len(n1)
	{
		//1. new [] 는 갯수만큼 생성자가 호출됩니다.
		arr2 = new Array1D[n1];
 
		//2. 생성자 초기화가 호출되지 않으면. 함수로 지정해주자. 
		//그런데. 이 값은 모든 생성자에 지정되지 않았다.
		//arr2->Setlen(n2);
 
		printf("len2              %d\n", len);
		printf("this2             %x    ", this);
		printf("생성자2 arr2    : %x  %x\n", arr2, arr2+1);
		for (int i = 0; i < n1; i++)
		{
			//3. 이 방법으로 생성자를 강제로 호출해서. 초기화 할 수 있습니다. 
			//하지만. 소멸자가 호출되는 문제가 있습니다.
//			arr2[i] = Array1D(2);
 
			//4. 결국. 이렇게하면. 모든 생성자를 초기화 할 수 있다.
			//각 클래스마다 최대값을 알려주어야 한다. 
			arr2[i].Setlen(n2);
		}
	}
	Array1D& operator[](int index)
	{
		printf("operator[2]   %x    %d\n", &arr2[ index], index);
		if (0 <= index && index < len)
		{
			return arr2[ index];
		}
		else
		{
			cout < < "Out of index range" < < endl;
			exit(0);
		}
	}
	int Getlen()
	{
		return len;
	}
	~Array2D()
	{
		cout < < "소멸자2 Array2" < < endl;
		delete[]arr2;
	}
};
 
 
 
int main(int argc, char** argv) 
{
	Array2D arr(4, 4);
 
 
	int cnt=0; 
	for (int k = 0; k < 2; k++)
	{
		for (int p = 0; p < 2; p++)
		{
			printf("-  k:%d   p:%d   cnt:%d\n", k, p, cnt);
			arr[k][p] = cnt;
//			arr[k] = Array1D(2);
//			printf("%x  %x  %d", &arr[k][p], &arr[k], arr[k][p]);
			printf("=\n");
			cnt++;
		}
	}
 
	printf("------------------------------------\n");
	printf("printf   arr[1][1]   :  %d\n\n", arr[1][1] );
	cout< < "cout     arr[1][1]   :  " < < arr[1][1] < < endl;
	printf("------------------------------------\n");
 
 
	return 0;
}
 
 
 
 
#if 0
 
len1              0
this              3d3f0c    생성자1 arr1   :  3d3f38
len1              0
this              3d3f14    생성자1 arr1   :  3d3f48
len1              0
this              3d3f1c    생성자1 arr1   :  3d3f58
len1              0
this              3d3f24    생성자1 arr1   :  3d3f68
len2              4
this2             22febc    생성자2 arr2    : 3d3f0c  3d3f14
len1              4
len1              4
len1              4
len1              4
-  k:0   p:0   cnt:0
operator[2]   3d3f0c    0
operator[1]   3d3f38    0   len:4
=
-  k:0   p:1   cnt:1
operator[2]   3d3f0c    0
operator[1]   3d3f3c    1   len:4
=
-  k:1   p:0   cnt:2
operator[2]   3d3f14    1
operator[1]   3d3f48    0   len:4
=
-  k:1   p:1   cnt:3
operator[2]   3d3f14    1
operator[1]   3d3f4c    1   len:4
=
------------------------------------
operator[2]   3d3f14    1
operator[1]   3d3f4c    1   len:4
printf   arr[1][1]   :  3
 
operator[2]   3d3f14    1
operator[1]   3d3f4c    1   len:4
cout     arr[1][1]   :  3
------------------------------------
소멸자2 Array2
소멸자1 Array1
소멸자1 Array1
소멸자1 Array1
소멸자1 Array1
 
--------------------------------
Process exited after 0.1687 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
#endif

댓글 첨부 파일: 

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

ozon1000의 이미지

어떤 컴파일러를 사용하시는지 모르겠으나, 근본적인 문제인 해제된 heap memory를 두번 해제하는것에 대해서는 근본적으로 해결이 안된듯 보입니다.

visual studio 2010에서는 동일한 오류가 생기네요.

shint의 이미지

메모리 해제는 정상적으로 보이는데요.

주소 값은 이상해보이네요.

DevC++ 로 확인해봤습니다.

이렇게 하니. 정확히 나옵니다.

#include <iostream>
 
#include<iostream>
#include<cstdlib>
 
using namespace std;
 
class Array1D
{
private:
	int* arr1;
	int len1;
public:
	Array1D(int n = 0) : len1(n)
	{
	}
	int& operator[](int index)
	{
		if (0 <= index && index < len1)
		{
			return *(&arr1[ index]);
		}
		else
		{
			cout < < "Out of index range" < < endl;
			exit(0);
		}
	}
	int Getlen()
	{
		return len1;
	}
	void Setlen(int n)
	{
		len1 = n;
		arr1 = new int[n];
		printf("this              %x    ", this);
		printf("생성자1 arr1   :  ");
 
		for (int i=0; i<n; i++)
		{
			printf("%x ", &arr1[ i]);
		}
		printf("\n");
	}
	~Array1D()
	{
		cout < < "소멸자1 Array1" < < endl;
		delete[]arr1;
	}
};
 
 
class Array2D
{
private:
	Array1D* arr2;
	int len;
public:
	Array2D(int n1, int n2) :len(n1)
	{
		arr2 = new Array1D[n1];
 
		printf("this2             %x    \n", this);
		printf("생성자2 arr2[]  : %x  %x\n", arr2, &arr2[1]);
		printf("생성자2 arr2    : %x  %x\n", arr2+0, arr2+1);
 
 
		for (int i=0; i<n1; i++)
		{
			Array1D * p = (Array1D*) &arr2[ i];
			p->Setlen(n2);
		}
 
#if 0
		//이건 하나씩 생성하려고 했는데 실패.
		for (int i=0; i<n1; i++)
		{
			Array1D * p = (Array1D*) &arr2[ i];
			for(int j=0; j<n2; j++)
			{
				p->Setlen(n2);
			}
		}
#endif
	}
	Array1D& operator[](int index)
	{
		if (0 <= index && index < len)
		{
			return arr2[ index];
		}
		else
		{
			cout < < "Out of index range" < < endl;
			exit(0);
		}
	}
	int Getlen()
	{
		return len;
	}
	~Array2D()
	{
		cout < < "소멸자2 Array2" < < endl;
		delete[]arr2;
	}
};
 
 
int MAX_X = 2;
int MAX_Y = 4; 
 
int main(int argc, char** argv) 
{
	Array2D arr(MAX_X, MAX_Y);
 
 
	int cnt=0; 
	for (int k = 0; k < MAX_X; k++)
	{
		for (int p = 0; p < MAX_Y; p++)
		{
			printf("-  k:%d   p:%d   cnt:%d       ", k, p, cnt);
			arr[k][p] = cnt;
//			arr[k] = Array1D(2);
			printf("main    %x  %x  %d", &arr[k], &arr[k][p], arr[k][p]);
			printf("=\n");
			cnt++;
		}
	}
 
	printf("------------------------------------\n");
	printf("printf   arr[1][1]   :  %d\n", arr[1][1] );
	cout< < "cout     arr[1][1]   :  " < < arr[1][1] < < endl;
	printf("------------------------------------\n");
 
 
	return 0;
}
 
 
 
 
#if 0
 
 
--------------------------------
2, 4 로 생성 
--------------------------------
 
this2             22febc
생성자2 arr2[]  : 3d3f0c  3d3f14
생성자2 arr2    : 3d3f0c  3d3f14
this              3d3f0c    생성자1 arr1   :  3d3a48 3d3a4c 3d3a50 3d3a54
this              3d3f14    생성자1 arr1   :  3d3a98 3d3a9c 3d3aa0 3d3aa4
-  k:0   p:0   cnt:0       main    3d3f0c  3d3a48  0=
-  k:0   p:1   cnt:1       main    3d3f0c  3d3a4c  1=
-  k:0   p:2   cnt:2       main    3d3f0c  3d3a50  2=
-  k:0   p:3   cnt:3       main    3d3f0c  3d3a54  3=
-  k:1   p:0   cnt:4       main    3d3f14  3d3a98  4=
-  k:1   p:1   cnt:5       main    3d3f14  3d3a9c  5=
-  k:1   p:2   cnt:6       main    3d3f14  3d3aa0  6=
-  k:1   p:3   cnt:7       main    3d3f14  3d3aa4  7=
------------------------------------
printf   arr[1][1]   :  5
cout     arr[1][1]   :  5
------------------------------------
소멸자2 Array2
소멸자1 Array1
소멸자1 Array1
 
--------------------------------
Process exited after 0.2562 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
 
--------------------------------
4, 2 로 생성 
--------------------------------
 
this2             22febc
생성자2 arr2[]  : 3d3f0c  3d3f14
생성자2 arr2    : 3d3f0c  3d3f14
this              3d3f0c    생성자1 arr1   :  3d3f38 3d3f3c
this              3d3f14    생성자1 arr1   :  3d3f48 3d3f4c
this              3d3f1c    생성자1 arr1   :  3d3f58 3d3f5c
this              3d3f24    생성자1 arr1   :  3d3f68 3d3f6c
-  k:0   p:0   cnt:0       main    3d3f0c  3d3f38  0=
-  k:0   p:1   cnt:1       main    3d3f0c  3d3f3c  1=
-  k:1   p:0   cnt:2       main    3d3f14  3d3f48  2=
-  k:1   p:1   cnt:3       main    3d3f14  3d3f4c  3=
-  k:2   p:0   cnt:4       main    3d3f1c  3d3f58  4=
-  k:2   p:1   cnt:5       main    3d3f1c  3d3f5c  5=
-  k:3   p:0   cnt:6       main    3d3f24  3d3f68  6=
-  k:3   p:1   cnt:7       main    3d3f24  3d3f6c  7=
------------------------------------
printf   arr[1][1]   :  3
cout     arr[1][1]   :  3
------------------------------------
소멸자2 Array2
소멸자1 Array1
소멸자1 Array1
소멸자1 Array1
소멸자1 Array1
 
--------------------------------
Process exited after 0.6227 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
#endif

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

raymundo의 이미지

에러가 난 이유는 이미 아셨으니... 해결책은 다른 분 댓글처럼 깊은 복사가 답일 수도 있겠습니다만,

어차피 Array1D 도 arr1 이 포인터로 구현되어서 arr1 이 가리키는 int 배열은 Array1D 객체와 별개의 위치에 저장되는 마당에,
굳이 Array2D 의 arr2 가 가리키는 공간을 Array1D 의 배열로 할 필요가 있나 싶은데요. 이걸 포인터의 배열로 하고 Array1D 객체들을 지역 변수가 생성되는 일 없이 처음부터 힙에 만들어버리시는 게 어떨런지.

고칠 부분만 원래 코드에 주석처리했습니다.

class Array2D
{
    private:
//        Array1D* arr2;
        Array1D** arr2;
        int len;
 
    public:
        Array2D(int n1, int n2) :len(n1)
    {
 
//        arr2 = new Array1D[n1];
        arr2 = new Array1D * [n1];
        for (int i = 0; i < n1; i++)
//            arr2[i] = Array1D(n2);
// 이제는 지역변수 Array1D 객체가 생겼다가 사라지는 일이 없으니 arr1 주소에 대해 두 번 delete 가 불릴 일도 없어짐
            arr2[i] = new Array1D(n2);    
    }
        Array1D& operator[](int index)
        {
            if (index < 0 || index >= len)
            {
                cout << "Out of index range" << endl;
                exit(0);
            }
 
//            return arr2[ index ];
            return *arr2[ index ];
        }
 
 
        int Getlen()
        {
            return len;
        }
 
        ~Array2D()
        {
            cout << "Array2" << endl;
// 아래 for 루프 추가
for (int i=0; i<len; i++)
    delete arr2[i];
            delete[]arr2;
        }
 
};

좋은 하루 되세요!

shint의 이미지

2, 4 로 출력
 
[2]   3d3f08
[1]            3d3a48
[1]            3d3a4c
[1]            3d3a50
[1]            3d3a54
[2]   3d3f18
[1]            3d3a98
[1]            3d3a9c
[1]            3d3aa0
[1]            3d3aa4
[2]   3d3f28
main   3d3a48 3d3f18
main   3d3a4c 3d3f18
main   3d3a50 3d3f18
main   3d3a54 3d3f18
main   3d3a98 3d3f28
main   3d3a9c 3d3f28
main   3d3aa0 3d3f28
main   3d3aa4 3d3f28
1
Array2
Array1
Array1
 
--------------------------------
Process exited after 0.5847 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
 
4, 2 로 출력
 
[2]   3d3a48
[1]            3d3f18
[1]            3d3f1c
[2]   3d3f08
[1]            3d3f38
[1]            3d3f3c
[2]   3d3f28
[1]            3d3f58
[1]            3d3f5c
[2]   3d3f48
[1]            3d3f78
[1]            3d3f7c
[2]   3d3f68
main   3d3f18 3d3f08
main   3d3f1c 3d3f08
main   3d3f38 3d3f28
main   3d3f3c 3d3f28
main   3d3f58 3d3f48
main   3d3f5c 3d3f48
main   3d3f78 3d3f68
main   3d3f7c 3d3f68
1
Array2
Array1
Array1
Array1
Array1
 
--------------------------------
Process exited after 0.2234 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

익명 사용자의 이미지

Array1D 객체의 복사를 피할 수 있는 방법으로는 이런 것도 있습니다.

class Array2D
{
private:
	Array1D* arr2;
	int len;
public:
	Array2D(int n1, int n2) :len(n1)
	{
		// operator new[]를 직접 호출하여 필요한 만큼의 메모리를 할당하고
		arr2 = reinterpret_cast<Array1D*>(operator new[](n1 * sizeof(Array1D)));
		for (int i = 0; i < n1; i++)
			// 위치 지정 new를 호출하여 바로 그 자리에 Array1D 객체를 생성합니다.
			// 여기서 생성자가 호출됩니다.
			new (arr2 + i) Array1D(n2);
	}
	Array1D& operator[](int index)
	{
		if (index < 0 || index >= len)
		{
			cout << "Out of index range" << endl;
			exit(0);
		}
		return arr2[ index ];
	}
	int Getlen()
	{
		return len;
	}
	~Array2D()
	{
		cout << "Array2" << endl;
		// 소멸은 생성의 역순으로. 먼저 루프를 돌며 모든 객체에 소멸자를 직접 호출해줍니다.
		for (int i = 0;i < len;i++)
			arr2[i].~Array1D();
		// 그 다음 operator delete[]를 호출하여 메모리를 해제합니다.
		operator delete[](arr2);
	}
};

raymundo님이 보여주신 방식과 위의 코드는 둘 다 Scott Meyers의 저서 More Effective C++ 중 항목 4에서 나오는 기법이죠. 그 항목에서 이 두 가지 방식 모두의 단점에 대해서도 설명하고 있으니 한 번 읽어보시길 권합니다.

어쨌거나, 이런 식으로 Array1D 객체의 복사를 피해간다고 해도 Array1D 객체가 현재 불완전한 구현이라는 건 변함이 없습니다. 깊은 복사를 해야 할 자리에 얕은 복사를 하는 기본 복사 생성자/대입 연산자가 있는 꼴이죠. 깊은 복사 코드를 짜 넣거나, 아니면 아예 복사를 불가능하게 막기라도 해야 합니다.

shint의 이미지

#if 0
 
2, 4 출력
 
arr2   3d3a68
arr2----prev
arr1   3d3ab8    3d3abc    3d3ac0    3d3ac4
arr2    new      3d3a68
arr2----next
arr2----prev
arr1   3d3f28    3d3f2c    3d3f30    3d3f34
arr2    new      3d3a70
arr2----next
main   3d3a68    3d3ab8    0
main   3d3a68    3d3abc    1
main   3d3a68    3d3ac0    2
main   3d3a68    3d3ac4    3
main   3d3a70    3d3f28    4
main   3d3a70    3d3f2c    5
main   3d3a70    3d3f30    6
main   3d3a70    3d3f34    7
5
Array2
Array1
Array1
 
--------------------------------
Process exited after 0.6239 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
 
4, 2 출력
 
 
arr2   3d3ed8
arr2----prev
arr1   3d3f28    3d3f2c
arr2    new      3d3ed8
arr2----next
arr2----prev
arr1   3d3f38    3d3f3c
arr2    new      3d3ee0
arr2----next
arr2----prev
arr1   3d3f48    3d3f4c
arr2    new      3d3ee8
arr2----next
arr2----prev
arr1   3d3f58    3d3f5c
arr2    new      3d3ef0
arr2----next
main   3d3ed8    3d3f28    0
main   3d3ed8    3d3f2c    1
main   3d3ee0    3d3f38    2
main   3d3ee0    3d3f3c    3
main   3d3ee8    3d3f48    4
main   3d3ee8    3d3f4c    5
main   3d3ef0    3d3f58    6
main   3d3ef0    3d3f5c    7
3
Array2
Array1
Array1
Array1
Array1
 
--------------------------------
Process exited after 2.14 seconds with return value 0
계속하려면 아무 키나 누르십시오 . . .
 
 
#endif

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

댓글 달기

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