My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


파이썬 유저를 위한 C++ (7) - 클래스 (1) - 개념, 접근제한자, this

10) 클래스(1)

파이썬의 클래스와 크게 다르지는 않습니다.

10.1 클래스와 인스턴스

  • 클래스 : 인스턴스를 찍어내는 틀
    • 멤버 함수는 모든 인스턴스가 공유함.
  • 인스턴스 : 해당 클래스 타입의 객체
    • 멤버 변수는 각 인스턴스마다 고유함.

10.2 클래스의 선언

class Book{
private: // 접근제한자, 바로 밑에서 설명할 예정
	int current_page;
    void Move(int page);
    
    void Move(int page){
        current_page = page;
        // 파이썬은 self.current_page라고 했는데 그러지 않아도 됩니다.
    }
}
  • 한 클래스에서 여러 접근제한자를 설정할 수 있음
  • 클래스 선언 안팎에서 멤버함수의 정의가 가능함.

클래스 선언 밖에서 멤버함수를 정의하기 위해서는 ::를 통해서 namespace를 제한해야합니다.

void Book::Move(int page){
    current_page = page;
}

10.3 접근제한자

파이썬에서는, 특정 인스턴스가 멤버변수에 접근하는 것을 제한하지 않습니다.

물론 __foo와 같은 방식으로 객체.__foo 형식의 접근을 막는 방법은 존재하지만, 기본적으로 다른 개념(맹글링)을 위해서 사용되는 것이지, private의 개념은 파이썬에서 존재하지 않는다고 합니다.

C++에서는 클래스의 접근제한자를 통해서 인스턴스에서 멤버변수(함수)로의 접근여부를 결정할 수 있습니다.

  1. public (구조체, 공용체의 기본값)

    • 객체의 public 변수에는 항상 접근 가능함.
  2. private (클래스의 기본값)

    • 객체의 private 변수에는 직접적으로 접근이 불가능함!
    class Book {
    private:
    	int id;
    };
    int main(){
        Book my_book;
        my_book.id = 23454654;
    }
    
    prog.cc: In function 'int main()':
    prog.cc:10:13: error: 'int Book::id' is private within this context
       10 |     my_book.id = 23454654;
          |             ^~
    prog.cc:6:6: note: declared private here
        6 |  int id;
          |      ^~
    
    • 그렇지만, public함수를 통해서 간접적으로 호출은 가능함.
    class Book {
    private:
    	int id;
    public:
    	void set_id(int new_id);
    };
       
    void Book::set_id(int new_id){
    	id = new_id;
    	std::cout << id; // 23454654
    }
       
    int main(){
        Book my_book = Book();
        my_book.set_id(23454654); 
    }
    
  3. protected

    이 부분은 파생클래스와 friend에 대해서 배워야 이해할 수 있을 것 같아서 나중에 적겠습니다.

10.4 this 포인터

this 포인터는 파이썬의 self와 비슷한 개념입니다.

다만 포인터이기 때문에 객체 자기자신의 주소를 가리킨다는 차이점이 있습니다.

void Book::set_id(int new_id){
	id = new_id;
	std::cout << id; // 23454654
}

방금 이런 식으로 객체의 id라는 멤버변수에 접근할 수 있었는데, 사실 이런 식으로 해도 똑같습니다.

void Book::set_id(int new_id){
	(*this).id = new_id;
	std::cout << id; // 23454654
}

왜냐하면 * (포인터 변수 역연산자)를 통해서 (*this)는 객체 자기자신을 뜻하기 때문입니다.

그리고, (*this).id*. 역할을 한꺼번에 할 수 있는 ->연산자도 있습니다.

void Book::set_id(int new_id){
	this->id = new_id;
	std::cout << id; // 23454654
}

-> 연산자는 보통 new 연산자를 통해 동적으로 할당된 객체에 접근하기 위해서 자주 쓰인다고 합니다.

10.5 this 포인터에 대한 추가적인 정보들

  • this 포인터를 사용하는 이유 : 객체 자기자신을 반환해야할 일이 있기 때문
const Book& Book::whichIsBetter(Book &another_book){
    return (this->id) > (another_book.id) ? (*this) : another_book 
} // 아이디가 더 큰 책 객체를 반환하는 함수.

int main(){
    Book my_book;
    Book your_book;
	
    my_book.set_id(12345);
    your_book.set_id(2345);
    
    std::cout << &my_book; // 0x7ffd0891842c
    std::cout << &(my_book.whichIsBetter(another_book)); // 0x7ffd0891842c
}

이 때, 반환타입을 Book&으로 지정하는 이유는 객체 그자체를 복사하지 않고 반환하기 위함입니다.

  • this 포인터는 언제나 포인터 상수
  • static 멤버 함수는 this 포인터를 가지지 않는다 : 파이썬의 static 함수와 비슷한 느낌.
comments powered by Disqus