2013년 11월 12일 화요일

[C++] 클래스의 private 접근지정자에 대한 고찰

이번엔 C++ 에 대한 글이다.
C++는 객체지향을 도입한 언어로서, 클래스를 정의할 수 있고 클래스에 대한 멤버 변수에 접근지정자를 부여할 수 있다. (private, protected, public)

private, protected, public의 의미는 알고 있다는 가정하에 이곳에서는 설명을 생략한다.

본론으로 바로 들어가서, 다음의 소스코드를 보면...

#include <iostream>

class Point
{
private:
    int x_;
    int y_;
public:
    Point() : x_(0), y_(0) {}
    Point(int x, int y) : x_(x), y_(y) {}
    Point(const Point &ref) : x_(ref.x_), y_(ref.y_) {}
    ~Point() {}

    void add_this(Point &ref)
    {
        x_ += ref.x_;
        y_ += ref.y_;
    }

    static Point add_new(Point &ref_a, Point &ref_b)
    {
        Point result;
        result.x_ = ref_a.x_ + ref_b.x_;
        result.y_ = ref_a.y_ + ref_b.y_;
        return result;
    }

    void show( const char *str )
    {
        std::cout << str << " : x[" << x_ << "] y[" << y_ << "]" << std::endl;
    }
};

int main()
{
    Point p1(100, 200);
    Point p2(300, 400);

    p1.show("p1");
    p2.show("p2");
    std::cout << std::endl;

    p1.add_this(p2);
    p1.show("p1");
    std::cout << std::endl;

    Point p3 = Point::add_new(p1, p2);
    p3.show("p3");
    std::cout << std::endl;

    return 0;
}

Point 클래스의 멤버 변수인 x_, y_ 는 private 변수이며, 자신의 멤버 함수들에 의해 접근하는 것은 당연히 허용한다. 그런데 문제는 멤버 함수 내부에서 다른 Point 클래스 객체를 생성하고 그 생성한 객체의 private 멤버 변수에 접근이 가능한지, 또는 멤버함수에서 다른 Point 클래스를 참조자로 받아 그 참조자로서 private 멤버 변수에 접근이 가능한지가 헷갈린다는 것이다.

위의 소스를 보면 add_new 함수는 자체적으로 Point 의 인스턴스를 생성한 후 그 인스턴스의 private 멤버변수에 바로 접근한다. 이 것이 컴파일이 가능하며 실행도 제대로 될까?

이상할 지도 모르지만, 위 소스는 아무런 문제 없이 컴파일 및 실행이 된다. 실행 결과는 다음과 같다.

p1 : x[100] y[200]
p2 : x[300] y[400]

p1 : x[400] y[600]

p3 : x[700] y[1000]

p3 클래스가 아무런 문제 없이 p1, p2의 각 멤버 변수의 합이 저장된 인스턴스로 생성이 되었다.

여기서 알 수 있는 사실은, 우리가 사용하는 접근제어자의 접근 제어 기준은 그때 그때 생성된 인스턴스의 기준이 아니라 클래스 기준이라는 것이다. 즉, 서로 다른 인스턴스이더라도 그 인스턴스가 동일한 클래스 타입이면 각 멤버는 서로의 private 멤버 변수에 접근이 가능하다는 이야기가 된다.

이것이 무언가 대단한 이야기 같지만, 가만히 생각해보면 당연한 이야기이다. C++에서 사용할 수 있는 friend 키워드도 그때그때 생성되는 인스턴스 기준이 아닌 틀 (클래스, 혹은 함수) 기준이다. 상속 또한 인스턴스가 아닌 클래스 기준이다.

다른 관점에서 접근하더라도 마찬가지이다. 만일 private 접근지정자가 클래스가 아닌 인스턴스 기준이라면 C++에서 기본적으로 제공해 주는(그래고 사용자 정의도 가능한) 복사생성자와 대부분의 연산자 오버로딩은 애초에 존재할 수가 없었을 것이다. ^^

뭔가 private이 private 같지 않아보일 지 모르지만, 이 사실은 우리가 private 에 대하여 좀 더 정확한 개념을 짚고 넘어가는 데에 도움이 될 것이다.

그렇다면 C++보다 객체지향성이 더욱 강한 JAVA는 어떨까?
결론부터 말하면... JAVA도 마찬가지이다. 아래 소스를 보자.

class A {
    private int val;
    public A() {
        this.val = 12345678;
    }
    public A(int i) {
        this.val = i;
    }
    
    public void test() {
        A a = new A();
        a.val = this.val;
        System.out.println("val a = " + a.val);
    }
    
    public void show() {
        System.out.println("val = " + this.val);
    }
}

public class Test {
    public static void main( String[] arags ) {
        System.out.println("Test !!!");
        A mainA = new A(87654321);
        mainA.show();
        mainA.test();
    }
    
}

class A 의 멤버 함수 test 안에서 다른 인스턴스 a를 선언한 후 a의 private 멤버 val에 직접 접근하여 값을 변경한다.
역시 정상 작동하며 결과는 다음과 같다.

Test !!!
val = 87654321
val a = 87654321

물론 JAVA에서 저런 형태의 소스코드를 작성할 경우가 드물 수도 있지만, private의 대상 범주를 좀 더 확실히 하는 데에 도움이 될 만한 소스코드일 것 같다.

결론은... 이 한마디로 갈음할 수 있을 듯 하다.

C++의 클래스는 자기 자신을 friend 클래스로 간주한다.

### Wafting ....... Done !!!
### ...
### ;;

댓글 2개:

  1. 전공학생인데 오늘 학교에서 시험보다 이 부분이 갑자길 헷갈렸는데 잘 알고 가요^^!! 감사합니다!

    답글삭제
  2. 고맙습니다. java에서 c++로 넘어가는 중에 좋은 글 잘 보고 갑니다.

    답글삭제