SturdyCobble's Study Note

[프로그래밍] 5.2 클래스(2) - 클래스 변수와 메소드 본문

휴지통/['19.06-'20.07]프로그래밍101

[프로그래밍] 5.2 클래스(2) - 클래스 변수와 메소드

StudyingCobble 2019. 9. 7. 22:09

NOTICE : 독학인 만큼, 잘못된 내용이 있을 수 있습니다. 내용 지적은 언제나 환영합니다.

더 이상 이 블로그에는 글을 올리지는 않을 예정입니다. 그렇지만 댓글을 달아주시면 최대한 답변드리고자 노력하겠습니다.


※이 글은 프로그래밍 언어에 대한 기초적인 이해를 가정하고 있습니다. 최소 프로그래밍 언어 하나 정도를 약간이라도 접해보시는 것을 추천합니다. 또한, 이 글은 심화 내용은 되도록 피하여 서술했습니다.

저번 글에서 인스턴스 변수에 대해 알아보았습니다. 이번 글에선 클래스 변수에 대해 이어서 알아보겠습니다.

 

인스턴스 변수는 인스턴스에 딸려오는 변수라면, 클래스 변수는 클래스 자체에 있는 변수에 접근하는 것이므로, 모든 인스턴스에 일괄적인 적용이 가능합니다. 다만, 만약 클래스 변수와 인스턴스 변수의 값이 다르다면, 인스턴스 변수로 접근할 때는 인스턴스 변수가 가지고 있는 값이, 클래스 변수로 접근할 때는 클래스 변수가 가지는 값을 사용하게 됩니다.

 

클래스 변수는 다음과 같이 사용합니다.

클래스명.변수

클래스명 :: 변수

 

 

Python에서는 별도의 조치 없이 클래스 변수를 바로 사용할 수 있지만, C++, Java에서는 static 키워드를 이용해주어야 합니다. 이는 정적(static) 공간에 변수가 선언된다는 의미로, 인스턴스가 소멸될 때 같이 소멸되며, 인스턴스 간 교환이 불가능한 인스턴스 변수와는 대비됩니다. 다음과 같은 형식으로 선언합니다.

class Test{
	public:
    	static int totNum;
};

class Test{
	public static int totNum;
};

(C++의 경우 클래스 멤버 변수는 생성과 동시에 초기화 불가.)

 

또한, C++의 경우 클래스 변수를 사용하기 위해 다음과 같은 선언이 추가적으로 필요합니다. 이는 실제 메모리상에 Num이란 변수를 위한 자리를 만들기 위함입니다.(main함수 밖 영역에 선언되야 합니다. 이에 대해서는 나중에 또 다룹니다.)

 

# include <iostream>

using namespace std;

class TestCpp{
	public:
    	static int totNum;
};

int TestCpp::totNum = 0; // 초기화
int main(){
	cout << TestCpp::totNum << endl;
	return 0;
}

 

메소드또한, 클래스의 멤버 변수와 같이 접근 지정자나 static 키워드를 사용할 수 있습니다. 그리고 멤버 변수와 같이 접근하여 사용할 수 있습니다. 

 

메소드의 특징 중 하나는 객체 자체를 전달 받을 수 있다는 것입니다.

# include <iostream>

using namespace std;

class TestCpp{
	public:
    	int num;
  void printer(TestCpp test){
    cout << test.num << endl << num << endl;
  }
};

int main(){
	TestCpp test01;
  TestCpp test02;
  test01.num = 10;
  test02.num = 20;
  test01.printer(test02);
  test02.printer(test01);
	return 0;
}


--------------출력 결과-----------
20
10
10
20

 

다른 객체의 변수를 출력하기 위해 객체를 전달받았습니다. 반대로, 객체 자신을 반환하거나, 객체 자신을 나타내기 위한 키워드도 존재합니다. 바로 'this' 키워드입니다.(C++,Java) Python은 self 키워드를 사용합니다. C++에선 this->var1과 같이->을 사용하여야 하는 차이점이 있습니다. 자세한 점은 나중에 다룹니다.

public class HelloWorld {
    public static void main(String[] args) {
        TestClass test1 = new TestClass();
        TestClass test2 = new TestClass();
        test1.num1 = 10;
        test2.num1 = 20;
        test2.printer(test1.printer(test2));
    }
}

class TestClass{
  public int num1;
  public TestClass printer(TestClass test){
    System.out.println(num1);
    System.out.println(test.num1);
    System.out.println(this.num1);
    return this;
  }
}
------------------출력 결과-------------------------
10
20
10
20
10
20

 

보다시피 객체 자신을 반환할 수 있음을 보여주며, this를 통해 인스턴스 변수를 접근할 수 있음을 보여줍니다. 위 결과를 좀 더 알기 쉽게 설명하자면 아래와 같습니다.

test2.printer(test1.printer(test2)); 에서
(test1.printer(test2)부분 실행)
10 -> test1.num1
20 -> test.num2
10 -> this.num1 = test1.num1
(test2.printer(    )부분 실행)
20 -> test.num2
10 -> test1.num1
20 -> this.num1 = test2.num1

한편, Python에서는 메소드에서 객체 자신의 멤버 변수를 사용하기 위해서 아래와 같은 문법을 필요로 합니다. 또한, 멤버 변수 자체를 그 이름으로만 접근하는 경우 오류가 뜹니다.

class TestClass:
    num1 = 10
    def cprinter():
        print("c")
    def printer(self,test): # self부분 추가
        print(self.num1)
        print(test.num1)
        return self

test1 = TestClass();
test2 = TestClass();

test1.num1 = 11
test2.num1 = 12

test1.printer(test2.printer(test1)) # 실제 전달하는 인수에는 self가 없음. 즉 인수는 test 한 개만 필요함.
test1.cprinter() # 오류발생

------------------출력-----------------
12
11
11
12
Traceback (most recent call last):
  File "E:\Python\test.py", line 17, in <module>
    test1.cprinter()
TypeError: cprinter() takes 0 positional arguments but 1 was given

위 오류의 원인은 self를 인자로서 받지 않는 메소드의 경우, 인스턴스를 통해 호출할 때 자동으로 전달되는 객체를 따로 처리할 수 없기 때문입니다. 위의 self가 없는 함수의 경우 TestClass.cprinter()와 같이 사용해야만 합니다. 그렇지 않은 경우 직접 객체를 전달해 줘야 합니다. ( test1.cprinter(test1))

 

 

위와 같이 객체를 전달하는 방식을 응용하면, 메소드로 다른 인스턴스의 속성 값을 변경하는 것도 가능합니다.

class Test:
    num1 = 0
    def augm(test):
        test.num1 += 1

test1 = Test();

for i in range(0,10):
    Test.augm(test1)

print(test1.num1)

---------------출력---------------
10

 

 

 

다음 글에선 생성자와 소멸자와 같은 개념들에 대해 알아보겠습니다.

Comments