[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번씩 호출됩니다.

[GIS] Mr.Tiler-Xr 3.0의 편집 기능 소개

맵 디자인 툴이자 타일맵 가공툴인 Mr.Tiler-Xr이 3.0으로 버전업 되었습니다. 대용량의 수치지도에 대해서도 빠른 속도로 도형 및 속성 데이터를 편집할 수 있으며 스냅핑 기능과 편집에 대한 Undo/Redo가 가능합니다.

편집 데이터는 일반적인 SHP을 그대로 사용함으로써 편집된 데이터를 SHP로 변환해 데이터를 교환할 필요가 없습니다. 아래의 링크는 Mr.Tiler-Xr의 편집 기능에 대한 시연 동영상에 대한 URL입니다.

http://www.gisdeveloper.co.kr/Mr.Tiler-Xr/mrtiler_demo2

Mr.Tiler의 편집 기능에 대한 시연에서 사용한 데이터는 2.5GB에 이르는 대한민국에 대한 전국 데이터입니다. 이미 잘 디자인된 지도 위에서 바로 데이터를 편집하고 추가할 수 있습니다.

하나의 툴만으로 공간 데이터를 편집하고 디자인하며 이를 타일맵으로 가공할 수 있는 툴은 지오서비스(GEOSERVICE)의 Mr.Tiler-Xr이 최초이며 가장 빠르고 뛰어납니다.

지오서비스는 웹기반의 GIS 솔루션 개발을 위한 맵엔진인 핑거아이즈와 모바일 기반의 맵엔진인 블랙포인트를 자체 기술력만으로 개발하여 지자체의 다양한 시스템 개발에 적용한, GIS 분야에서 최고의 기술력을 갖춘 GIS 소프트웨어 개발회사입니다.

[JavaScript] 클래스 상속

자바스크립트에서 클래스의 상속 개념을 정리해 보고자 합니다. 먼저 크기값(width, height) 만을 갖는 Rectangle라는 클래스가 있고 이 클래스를 상속받으면서 위치값(x, y)를 갖는 PositionedRectangle 클래스를 만들어 보는 예를 통해 정리하고자 합니다. (이 글은 인사이트의 자바스크립트 완벽가이드 내용을 읽고 이해하여 짧게 요약한 글입니다)

가장 먼저 Rectangle이라는 클래스 정의 코드입니다.
function Rectangle(w, h)
{
    this.width = w;
    this.height = h;
}

Rectangle.prototype.area = function()
{
    return this.width * this.height;
}

Rectangle.prototype.toString = function() 
{
    return "[" + this.width + ", " + this.height + "]";
}

생성자 함수에서 크기값에 대한 인자 w, h를 받습니다. 그리고 area라는 매서드와 toString이라는 매서드를 정의하고 있습니다. 이후에 Rectangle 클래스를 상속받아 toString 함수를 재정의할때 부모 클래스의 toString을 호출하는 방법을 살펴보도록 하겠습니다.

자, 이제 PositionedRectangle이라는 클래스를 Rectangle 클래스를 상속받아 정의하는 코드를 보겠습니다.
function PositionedRectangle(x, y, w, h)
{
    Rectangle.call(this, w, h);
    
    this.x = x;
    this.y = y;
}

3번 코드에서 상속받을 Rectangle 클래스를 지정합니다. 그리고 PositionedRectangle의 부모 클래스가 Rectangle이라는 것을 명확히 하도록 다음 코드를 추가합니다.

PositionedRectangle.prototype = new Rectangle();

위의 코드의 문제점은 PositionedRectangle의 생성자 함수까지도 Rectangle로 지정된다는 점입니다. PositionedRectangle의 생성자 함수를 다시 자기의 것으로 지정되도록 다음 코드를 추가합니다.

PositionedRectangle.prototype.constructor = PositionedRectangle;

또 한가지 문제가 있는데, 그것은 중복된 데이터입니다. PositionedRectangle는 Rectangle로부터 상속을 받았으므로 Rectangle의 width와 height 데이터를 갖습니다. 그런데 이 값이 각각 2개씩 중복(PositionedRectangle에 하나, PositionedRectangle.prototype에 하나)되어 갖고 있습니다. 해서 중복되지 않도록 제거해 주는 코드가 필요합니다.

delete PositionedRectangle.prototype.width;
delete PositionedRectangle.prototype.height;

이제 옵션으로 PositionedRectangle 자신의 함수를 추가하는 코드입니다.

PositionedRectangle.prototype.contains = function(x y)
{
    return (x > this.x && x < this.x + this.width 
        && y > this.y && y < this.y + this.height);
}

끝으로 부모 클래스의 메서드 함수를 재정의할때 부모 클래스의 메서드 함수를 호출하는 방법입니다. toString 함수를 예로 정리하면, PositionedRectangle에 다음처럼 toString 함수를 재정의할 수 있습니다.

PositionedRectangle.prototype.toString = function()
{
    return "(" + this.x + ", " + this.y + ") " 
        + Rectangle.prototype.toString.apply(this);
}

[JavaScript] 모조 클래스

자바스크립트에서 클래스(엄격히 말해 모조 클래스)를 작성하는 내용에 대한 정리입니다. 일반적으로 다음과 같이 5단계에 걸쳐 클래스를 정의합니다.

  1. 생성자 함수 정의
  2. 인스턴스 프로퍼티 정의
  3. 인스턴스 매서드 정의
  4. 클래스 매서드 정의
  5. 클래스 프로퍼티 정의

위의 5단계를 Complex라는 클래스를 자바스크립트 방식으로 정의해 보는 예를 통해 정리하면, 먼저 생성자 함수의 정의는 다음과 같습니다.

function Complex(real, imaginary)
{
    // ..
}

클래스의 이름은 첫자를 대문자로 하여 비록 함수이지만 클래스라는 의미를 강조합니다. 다음으로 인스턴스 프로퍼티를 정의하면…

function Complex(real, imaginary)
{
    this.x = real;
    this.y = imaginary;
}

생성자 함수 안에 this라는 키워드를 통해 새로운 인스턴스 변수를 추가합니다. 이제 인스턴스 매서드를 정의하는데, 매서드는 하나의 클래스에 대한 모든 인스턴스가 공유하도록 하기 위해 prototype 객체를 사용하며 예로 다음과 같습니다.

Complex.prototype.magnitude = function()
{
    return Math.sqrt(this.x * this.x + this.y * this.y);
}

이제 클래스 매서드를 추가하는 방법입니다. 클래스 매서드는 인스턴스를 대상으로 하는 것이 아니므로 this를 사용할 수 없으며 생성자 함수(자바스크립트에서 클래스) 이름으로 접근할 수 있습니다. 예로 다음과 같습니다.

Complex.sum = function(a, b)
{
    return new Complex(a.x+b.y, a.y+b.y);
}

끝으로 클래스 프로퍼티입니다.

Complex.ZERO = new Complex(0, 0);

이 글은 인사이트 출판사의 자바스크립트 완벽가이드를 참고로 하여 정리한 글입니다.

[JavaScript] 함수 인자의 타입 검사

자바스크립트에서 함수를 만들어 사용할때 그 함수의 인자에 대한 타입은 암묵적인 약속입니다. 이 말은 “이 함수의 인자의 타입은 … 이다”라는 개념에 지나지 않다는 의미입니다. 그러한 개념에서 벗어나 보다 엄격하게 그 타입을 지키도록 하는 방어책이 필요할때 아래의 예를 참고하고자 이 글로 정리해 둡니다.

function sum(a /* Array */)
{
    var isArray = (a instanceof Array);
    var isLikeArray = (a && typeof a == "object" && "length" in a);
  
    if(isArray || isLikeArray)
    {
        var total = 0;
    
        for(var i=0; i        {
            var element = a[i];
            if(!element) continue;
            if(typeof element == "number") 
                total += element;
            else 
                throw new Error("원소는 숫자여야 함");
        }

        return total;
    }
  
    throw new Error("인자는 배열이어야 함");
 }

var a = [1,2,3,4,5];
try 
{
    document.write(sum(a));
} 
catch(e)
{
    document.write(e.message);
}

3번 코드가 전달 받은 인자의 타입이 배열인지를 확인하는 것이고 4번은 배열과 비슷한 객체인지, 즉 length 프로퍼티를 제공하는지를 확인하는 코드입니다. 14번 코드는 배열의 원소가 숫자인지를 확인하는 코드입니다.

이 코드는 인사이트의 JavaScript 완벽 가이드라는 책의 코드를 조금 변형한 것입니다. 자바스크립트에 관해 좋은 책이라고 생각합니다.