행렬 각 원소를 입력 받고 행렬 출력 및 역행렬을 이용한 연립방정식의 해 등을 구하는 함수를 구현해보자.

사전지식:

1. pointer 개념

2. structure 구조와 개념

3. class 구조와 개념

4. friend keyword

5. overator overloading

6. pass by value, pass by reference

 

구조체와 클래스를 이용한 행렬 클래스 만들기 프로젝트는 총 6번에 걸쳐서 발전시킬 것이다. 차차 발전시킬 부분이 처음엔 다소 미흡해 보일 수 있으나 포인팅하여 모호함을 최대한 줄이려고 노력하겠다. 최종본을 원하면 (...url...)

 

앞서 "145. 구조체와 클래스의 비교"에서 우리는 structure와 class가 비슷하지만 다른 점을 살펴 보았다. 그러면 실제로 행렬을 구현하면서 structure와 class의 application에 대해 좀 더 자세히 다뤄보자.

 

structure는 data grouping을 한다고 했고, class는 data grouping에 더불어 member function과 OOP를 구현할 수 있다고 했다.그러면 행렬의 연산은 class를 통해 구현할 수 있고, data grouping은 structure를 통해 구현하여 역할을 명확하게 나눠 표현해볼까한다.

 
struct strMat {
    double **mat;    //pointer to pointer Type to dynamically assign
    int row;
    int col;
    strMat(int _row, int _col, double** _mat)    //constructor
    {
        row = _row;
        col = _col;

        mat = new double*[row];        //dynamic assign
        int rowlen = _col * sizeof(double);    //for memset
        for (int i = 0; row; i++)
        {
            mat[i] = new double[col];    //assign an array in each row element: array of array
            memset(mat[i], 0, rowlen);    //initialize one-dimensioanl array using memset 
            if (_mat) memcpy(mat[i], _mat[i], rowlen);    //copy if get '_mat' argument
        }
    }
    ~strMat()    //delete against memory leak
    {
        for (int i = 0; i < row; i++)
        {
            delete[] mat[i];
        }
        delete[] mat;
    }

};

부담감 없이 보길 바란다. 간단하게 위의 structure 또한 member field, constructor와 destructor로 구성되어 있다. 정말로 structure는 data grouping의 목적으로 쓰였음을 알 수 있다.

*CODE DESCRIPTION:

line3,4,5:

member field에 해당하는 부분이고, 행렬의 데이터 구성요소들이라고 생각하면 된다.

2차원배열(array of array)을 동적으로 할당하기 위해 pointer to pointer type(type**)으로 정의함

line6~19:

constructor 부분. 행렬의 필수 데이터인 row와 column data 그리고 array of array를 받을 수 있는 double pointer(pointer to pointer의 다른 표현)로 객체를 받음.

line7,8:

입력받은 _row, _col로 structure의 member field를 채운다.

우리는 **로 pointer level이 2임을 알 수 있고, mat에 row의 크기를 갖는 double* 타입의 배열을 동적 할당한다.

 (※ double(*)[row]와 구분)

우리는 row 크기를 갖는 array를 갖고 있다. 우리는 array를 index를 통해 접근할 수 있음을 이미 알고 있다.

line12:

memset과 memcpy의 3번 째 argument로 넣어줄 variable을 정의 세 번째 인자는 number of characters로 총 bytes 수를 의미

line13~18:

아까 선언한 row크기를 갖는 array의 각 element에 또 array를 for문을 통한 index로 접근해서 또 array를 정의해주고 초기화     및 대입을 한다. mat[i]로 접근하면서 pointer 마지막 레벨이므로 col의 크기를 갖는 double타입의 array를 정의해준다.

memset으로 초기화하고 만약에 mat object가 들어오면 해당 mat에 있던 데이터들을 그대로 copy한다. (Note. 이때 memset과 memcpy는 1차원 배열 단위로 각각 initialize 하고 copy하는 C-style 함수.)

line20~27. destructor부문. main에서 동적할당이 이루어지고 해제가 모두 이루어졌다면, class에 내에서 동적할당된 메모리는 destructor에서 해제를 정의해주면 된다.

이제 행렬(matrix)에 관한 data definition는 모두 이루어졌다. 이제 class로 무엇을 해야할 지 짐작이 가는가? 행렬에 값만 넣으면 모든 게 끝인가? 당연히 아니다. 우리는 행렬들을 가지고 곱하기 연산, row의 길이와 column의 길이를 얻어오는 연산 등 얼마든지 다양한 기능들을 구현 할 수 있다.  여기서는 row의 길이와 column의 길이를 얻어오고 행렬 특정 element의 값을 출력하는 3개의 기능을 구현해보자. 더불어 <<를 overloading하여 class 객체를 출력해보자( 객체 사이의 기본 연산은 불가능하지만 operator overloading을 이용하면 가능함을 이미 앞서 배웠음).

 
class Matrixs {
private:
    strMat* _matrix;    //declare a structure as a member field
public:
    Matrixs(int row, int col)    //constructor
    {
        _matrix = new strMat(row, col, 0);
    }
    ~Matrixs()                    //destructor
    {
        cout << "Matrix end~" << endl;
        delete _matrix;
    }
    //member function
    int getRow()const { return _matrix->row; }
    int getCol()const { return _matrix->col; }
    double getVal(int row, int col)const
    {
        return _matrix->mat[row][col];
    }
    //operator overloading
    friend ostream& operator << (ostream& ostrm, const Matrixs& m);
};


ostream& operator << (ostream& ostrm, const Matrixs& m)    //operator overloading
{
    for (int i = 0; i < m._matrix->row; i++)
    {
        for (int j = 0; j < m._matrix->col; j++)
        {
            ostrm << m._matrix->mat[i][j] << "\t";    //array of array structure can be assigned with indices
        }
        ostrm << endl;
    }
    return ostrm;
}

 *CODE DESCRIPTION:

line3:

우리는 structure를 통해 data grouping을 모두 끝냈다. 그렇다면 더이상의 data grouping은 할 필요없기때문에 class가

그 structure를 그대로 갖길 원한다. 따라서 strMat type의 object를 member field로 선언해주면 되는데, 여기서 이 strMat

을 동적으로 생성하기 위해 *가 필요하다.

line6~9: class constructor 부분.

parameter로 row, col 값을 요구. 구조체 strMat을 argument로 받은 값을 가지고 strMat 타입의 object를 동적으로(heap memory영역에) 할당. strMat의 constructor에 정의된 대로 class의 constructor를 통해 받은 row, col 값을 그대로 대입. 그리고 세번째 argument로 0은 strMat의 세번째 parmamet인 double** _mat으로 들어가게 되며, pointer 이든 pointer to pointer든 주소값을 가지며, 주소값이 0을 의미한다. 아무런 Matrix객체가 들어오지 않음을 의미한다. strMat의 constructor 부분 맨 마지막 부분의 if(_mat)분기에 접근하지 못한다. 아무런 객체가 들어오지 않았을때 strMat에서는 바로위에 memset으로 먼저 항상 초기화하여 문자가 발생하지 않도록 하였다. (<<if분기로 빠지는 부분은 우리가 앞으로 발전시켜야할 부분이다.)

line9~13: class destructor 부분.

line22,26~37: <<operator overloading.              (링크 필요)

<< 으로 Matrix class object도 출력하게 하기 위함.  

int main()
{

    Matrixs *mat = new Matrixs(10,5 );    //create Matrixs object dynamically assigned
    cout << *mat << endl;
    cout << "--------------------------------" << endl;
    Matrixs mat2(10,5);                    //create Matrixs object statically assigned
    cout << mat2 << endl;
    delete mat;
}
 

 *CODE DESCRIPTION:

line4: Matrixs object를 동적할당

line5: 우리는 << operator overloading 을 통해 class 객체를 출력할 수 있도록 하였고 pointer 변수가 가리키는 object를 출력

line7: 이번엔 class 객체를 정적 할당

line8: line5와 같은 이유로 출력 가능하고, 출력

line9: line4에서 동적할당한 객체를 memory leak를 방지하기 위해 memory 할당 해제(memory deallocate)

Q. main에서 두 개의 object를 생성했다. 하나는 동적할당 또 하난 정적할당이다. 어떤 object가 먼저 memory deallocate를 할까?

 

 

Concept1. Pointers to Pointers

일반적으로, type modifier(* or &)가 declarator에 적용되는 횟수에는 제한이 없다.

(declarator: The part of a declaration that includes the name being defined and an optional type modifier)

 

하나 이상의 modifier가 있을 때, 그것들은 논리적인 방식으로 합쳐지지만 항상 분명하진 않다.

하나의 예로, a pointer를 생각해보자. a pointer는 memory 안에 있는 하나의 object이다. 그래서 여느 object처럼 a pointer 또한 주소를 가지고 있다. 그러므로 우리는 다른 pointer to pointer의 주소를 저장할 수 있수 있게 된다.

 

*로 각 pointer level을 가리키게 된다. 즉, 우리는 pointer to a pointer를 **로 쓰고, pointer to a pointer to a pointer를 *** 써서 제한없이 쓸 수 있다. 

(array of array에 대한 pointer 구조 그림 추가)