[C++] 복사생성자, 소멸자, 대입연산자

C++을 학습하던 때에 제법 깊이 있게 생각하며 봤던 C++ 내용 중 복사생성자와 소멸자 그리고 대입연산자가 있었습니다. 마치 스타크래프트에서 등장하는 유닛들의 이름을 한글화 해 놓은 것은 이 3가지에 대해서 정리해 봅니다.

기본적으로 C++은 복사생성자, 소멸자, 대입연산자를 프로그래머가 정의해 놓지 않을 경우 정해진 기본 기능을 수행하는 복사생성자와 소멸자, 대입연산자를 만들어 놓는다고 가정할 수 있습니다.

기본 기능에 대한 복사생성자는 해당 클래스의 맴버 변수의 값을 그대로 복사합니다. 또한 기본 기능에 대한 소멸자는 해당 클래스의 맴버 변수의 소멸자를 호출해 줍니다. 그리고 기본기능에 대한 대입연산자는 해당 클래스의 맴버 변수의 값을 그대로 복사합니다.

여기서 언급된 맴버 변수의 값에 대한 복사라함은 단순한 값의 복사로써 만약 맴버 변수가 포인터일 경우 그 변수가 가르키고 있는 값까지 복사하는것이 아닌 주소값만을 복사한다는 의미입니다. 바로 이 포인트에 대한 부분이 C++에서 복사생성자와 소멸자, 대입연산자의 중요함이 강조되는 부분입니다.

아래의 코드는 이들 세가지(복사생성자, 소멸자, 대입연산자)에 대한 작성방법 및 이들을 제공하지 않았을 경우 어떻게 이들 세가지가 호출되는지를 보여주는 예입니다. 작성방법은 클래스 M에서 파악할 수 있고 호출 순서와 여부는 클래스 X에서 파악할 수 있습니다. 클래스 X는 이들 셋을 정의하고 있지 않으므로 C++에서 기본적으로 제공하는 기능에 대해 수행된다고 확신할 수 있습니다.

먼저 클래스 M에 대한 코드입니다.

class M {
public:
    M() {
        cout << "M::default-ctr is called" << endl;
    }

    M(const M&) {
        cout << "M::copy-ctr is called" << endl;
    }

    ~M() {
        cout << "M::dtr is called" << endl;
    }

    M& operator=(const M&) {
        cout << "M::oper= is called" << endl;
        return *this;
    }
};

여기서 기본생성자(M::default-ctr is called를 출력하는 함수)가 추가로 제공되고 있습니다. C++은 프로그래머가 생성자를 추가하지 않으면 기본생성자를 자동으로 추가해 주지만, 여기서는 복사생성자를 추가하고 있음으로 프로그래머가 직접 기본 생성자를 추가해 주어야 합니다.

다음은 클래스 X이며 이 클래스는 복사생성자와 소멸자, 대입연산자를 프로그래머가 제공하지 않으며 단순히 맴버 변수로써 클래스 M만을 가지고 있습니다.

class X {
    M m;
};

이제 클래스 X를 생성하고 대입하는 코드를 통해 복사생성자와 소멸자, 대입연산자의 호출을 확인해 보도록 하겠습니다.

X x;
X y = x;
x = y;

실행 결과는 다음과 같습니다.

사용자 삽입 이미지
1번 코드에 의해서 M::default-ctr is called가 표시되며 2번 코드에 의해서 M::copy-ctr is called가 호출됩니다. 그리고 3번 코드에 의해서 M::oper= is called가 호출됩니다. 그리고 이 프로그램의 유효범위가 종료됨으로써 직역변수로 선언된 x와 y에 대한 소멸자가 각각 1번씩 호출됩니다.

[C#] LINQ를 이용한 XML 파싱 쿼리

“XML에서 원하는 데이터 검색하기”에서 XML을 쿼리하는 방법을 학습한 뒤에 LINQ를 이용해 XML을 쿼리 하는 방법도 학습해 보았습니다. “XML에서 원하는 데이터 검색하기”에서 사용한 동일한 XML 데이터를 이용했고.. 그 쿼리 대상도 동일하게 했습니다. 다시 한번 쿼리 대상을 살펴보면…

  1. <book>의 개수는?
  2. 두번째 <book>의 <title>의 값은?
  3. 두번째 <book>의 genre의 값은?
  4. 두번째 <book>의 <author>의 <first-name>의 값은?

위에 대한 결과는 다음과 같습니다.

  1. 3
  2. The Confidence Man
  3. novel
  4. Herman

Linq를 이용해 위의 쿼리 대상의 결과를 얻어 내는 코드를 하나 하나 들어 보기에 앞서 LINQ를 XML에 적용하기 위한 준비 코드가 필요하며 다음과 같습니다.

XmlTextReader reader = new XmlTextReader("books.xml");
XElement xml = XElement.Load(reader);

var books = from item in xml.Descendants("book") select item;

여기서 4번 코드가 바로 LINQ입니다. 사실.. XML에서 원하는 데이터를 쿼리 하는 기능은 LINQ의 기능중 작은 부분을 차지 하고 있어 그리 크게 느껴지지 않습니다.

뭐 여튼…… 이제 우리의 목표를 하나 하나 이뤄보겠습니다. 먼저 “<book>의 개수는?”에 대한 코드입니다.

Console.WriteLine(books.Count());

다음은 “두번째 <book>의 <title>의 값은?”에 대한 코드입니다.

Console.WriteLine(
    books.ElementAt(1).Descendants("title").ElementAt(0).Value
);

그리고 세번째로 “두번째 <book>의 genre의 값은?”에 대한 코드입니다.

Console.WriteLine(books.ElementAt(1).Attribute("genre").Value);

끝으로 “두번째  <book>의 <author>의 <first-name>의 값은?”에 대한 코드입니다.

Console.WriteLine(
    books.ElementAt(1).Descendants("author").Descendants("first-name")
        .ElementAt(0).Value
);

제가 LINQ를 제대로 이해하고 있지 않은 이유일까요? LINQ를 이용해 XML에서 원하는 데이터를 쿼리하는 간결하고 직관적인 코드가 존재하지 않을까… 하는 생각이 듭니다. 바로 이런 이유가 LINQ의 이유 중에 하나이니까요… 앞서 언급했지만.. XML에서 원하는 데이터를 쿼리 하는 기능은 LINQ의 기능중 작은 부분을 차지 하고 있는듯하여 LINQ를 통해 XML을 쿼리하는 방식은 그리 매력적이라고 느껴지지 않습니다..