객체지향 부록 #2 생성자와 소멸자, this, 접근제어 지시자

기타(임시) · 2020. 7. 31. 09:41

 

1. 생성자와 소멸자

2. this

3. 접근제어 지시자

 

 

 

 

1. 생성자와 소멸자

 

- 생성자

 

생성자는 클래스의 인스턴스 (사람이라는 클래스와 5000만 명의 한국 사람들 각각의 인스턴스: 클래스의 정의를 통해 만들어진 객체)를 메모리에 할당할 때 초기화 방식을 담당한다.

 

new로 인스턴스를 동적할당 할 때 생성자가 호출된다.

 

또한, 생성자는 반환값이 없고 한번에 여러번 정의 될 수 있다.

 

ex)

class이름 :: class이름() // class 이름과 함수 이름이 동일하다

{

    생성자 몸체

}

 

ex)

#include <iostream>
using namespace std;

 

class Seoul
{
public:
   Seoul(); // class명과 동일한 디폴트(기본) 생성자. 공개(public) 멤버함수
   void dataShow(); // 공개 멤버함수

 

private:
   unsigned int Temp;  // 비공개(private) 멤버변수
   unsigned int Population; // 비공개 멤버변수
};

 

Seoul::Seoul() // 생성자 정의
{
   Temp = 30;          // 멤버변수 초기화
   Population = 1500;   // 멤버변수 초기화
}

 

void Seoul::dataShow()
{
   cout << "서울의 온도는 " << Temp << "도 입니다." <<endl;
   cout << "서울의 인구는 " << Population << "만 입니다."<< endl;
}

 

int main()
{
   Seoul happy; // 생성자 Seoul()이 실행됨. -> default 생성자에 대한 정의가 없으면 변수 초기화도 없을 듯
   happy.dataShow();
   return 0;
}

 

- 소멸자

 

클래스의 인스턴스가 더 이상 필요하지 않을 때 메모리에 존재하는 인스턴스 해제를 위해 호출한다.

 

객체가 파괴될 때 소멸자는 자동으로 호출된다.

 

중요한 것은 객체의 소멸뿐 아니라 객체가 생성될 당시 동적으로 할당된 환경들의 소거작업에 대한 정의가 소멸자에 구현 되어야 한다.

 

ex)

class이름 :: ~class이름() // class 이름과 함수 이름이 동일하다

{

    생성자 몸체

}

 

ex)

#include <iostream>
using namespace std;

 

class Seoul
{
public:      // 공개 선언
   Seoul(); // class명과 동일한 디폴트(기본) 생성자. 공개 멤버함수
   ~Seoul(); // 공개 멈버함수. 소멸자
   void dataShow(); // 공개 멤버함수

 

private:                // 비공개 선언
   unsigned int Temp;  // 비공개 멤버변수
   unsigned int Population; // 비공개 멤버변수
};

 

Seoul::Seoul() // 생성자 정의
{
   cout << "생성자 호출" << endl;
   Temp = 30;          // 멤버변수 초기화
   Population = 1500;  // 멤버변수 초기화
}

 

Seoul::~Seoul() // 소멸자 정의
{
   cout << "소멸자 호출" << endl;
}

 

void Seoul::dataShow()
{
   cout << "서울의 온도는 " << Temp << "도 입니다." <<endl;
   cout << "서울의 인구는 " << Population << "만 입니다."<< endl;
}

 

int main()
{
   Seoul happy; // 객체 생성. 생성자 Seoul()이 실행됨.
   happy.dataShow();
   return 0;
}

-> 소멸자는 main 함수에 따로 호출하지 않아도 실행이 된다. (프로그램이 끝날 때?)

 

생성자와 소멸자는 클래스에 명시하지 않는 경우 디폴트 생성자와 디폴트 소멸자가 사용된다.

 

 

- 생성자의 종류

 

1) 기본 생성자 (default)

 

생성자를 만들지 않았을 경우 컴파일러가 자동으로 만들어 주는 생성자이다.

 

초기화를 할 필요가 없다면, 생성자 또한 고려할 필요가 없다.

 

굳이 디폴트 생성자를 이용하여 객체를 만들 필요가 있을 때는 다음과 같이 사용할 수 있다.

 

ex1) Seoul seo;

ex2) Seoul seo = Seoul();

 

2) 매개변수를 가진 생성자

 

생성자 안에 매개변수를 가지는 형태이다.

 

이때 매개변수 데이터 형의 종류와 개수를 다르게 하여 (함수 오버로딩) 객체 운용을 다양하게 할 수 있다.

 

ex)

#include <iostream>
using namespace std;

 

class Jeju
{
   int x;
   int y;

 

public:
 
 Jeju()
 {
    x=0;
    y=0;
 }
 
 Jeju(int a, int b)
 {
    x=a;
    y=b;
 }

 

 Jeju(double a, double b)
 {
    x=a;
    y=b;
 }

 

 void show()
 {
    cout << x << endl;
    cout << y << endl;
 }

 

};


int main()
{
   Jeju happy(3.7, 3.7);
   happy.show();
   return 0;
}

 

-> class 멤버 변수 형이 int이기 때문에 출력 결과는 3, 3이 나온다.

 

** 클래스 생성자를 인자로 받는 형태로 프로그램을 구성했을 때 해당 클래스의 객체를 생성하면서 전달인자(생성자)를 넘기지 않으면 컴파일러는 "일치하는 생성자가 없다."라는 메시지를 보낸다. 이때는 빈 공백의 생성자를 만들어 넣어야 한다.

** 생성자를 클래스 내부에서 정의해 선언했다면 소멸자도 반드시 정의해야 한다.

 

 

2. this

 

this는 객체 자신에 대한 포인터로서 클래스의 멤버 함수 내부에서만 사용이 가능하다. 

 

하지만, static 멤버함수에서는 사용할 수 없다.

 

this는 변수는 아니고 객체의 주소를 컴파일러에게 보이지 않게 전달하는 주소이다.

 

C++에서 하나의 클래스에서 생성된 인스턴스는 각각 독립된 메모리 공간에 저장된 자신만의 멤버 변수를 가지지만, 멤버 함수는 모든 인스턴스가 공유한다.

 

따라서, 하나의 클래스를 이용해 두 개의 인스턴스를 생성해도, TickerBook()이라는 멤버 함수는 하나만 존재한다.

-> 더 두꺼운 책의 이름을 출력해 주는 TickerBook() 멤버 함수는 두 인스턴스가 같이 공유하게 된다.

 

ex)

* TickerBook() 멤버 함수의 정의

const Book& Book::ThickerBook(const Book& comp_book)

{

    if (comp_book.total_page_ > total_page_)

    {

        return comp_book;

    }

    else

    {

        return ???; // 자신을 호출한 인스턴스를 반환하는데 알 수가 없음.

    }

}

 

* 해당 멤버 함수는 다음과 같이 호출됩니다. 

web_book.ThickerBook(html_book);

-> 이때 ThickerBook() 멤버 함수는 자신을 호출한 객체가 web_book 객체임을 인수를 통해 전달받아야 한다.

그래야 else 문에서 반환값을 정확히 명시할 수 있기 때문!

 

- this 포인터

 

위와 같은 이유에서 C++의 모든 멤버 함수가 자신만의 this 포인터를 가지고 있다.

 

this 포인터는 해당 멤버 함수를 호출한 객체를 가리키게 되며, 호출된 멤버 함수의 숨은 인수로 전달된다.

 

이렇게 하면 호출된 멤버 함수는 자신을 호출한 객체가 무엇인지 정확히 파악할 수 있다.

 

ex)

* this 포인터를 사용하여 위의 예제를 재정의 하면 다음과 같다.

const Book& Book::ThickerBook(const Book& comp_book)

{

    if (comp_book.total_page_ > total_page_)

    {

        return comp_book;

    }

    else

    {

        return *this; // 자신을 호출한 인스턴스를 반환함.

    }

}

-> this는 포인터이므로, 반환할 때는 참조 연산자 (*)을 사용하여 호출한 객체 그 자체를 반환해야 한다.

 

ex)

#include <iostream>
using namespace std;

class Book
{
private:
int current_page_;
void set_percent();
public:
Book(const string& title, int total_page);
string title_;
int total_page_;
double percent_;
void Move(int page);
void Open();
void Read();
const Book& ThickerBook(const Book&); // ThickerBook() 함수의 원형 
};

int main(void)
{
Book web_book("HTML과 CSS", 350);
Book html_book("HTML 레퍼런스", 200);

cout << web_book.ThickerBook(html_book).title_; // 더 두꺼운 책의 이름을 출력함. 
return 0;
}

Book::Book(const string& title, int total_page)
{
title_ = title;
total_page_ = total_page;
current_page_ = 0;
set_percent();
}

void Book::set_percent()
{
percent_ = (double) current_page_ / total_page_ * 100;
}

const Book& Book::ThickerBook(const Book& comp_book)
{
if (comp_book.total_page_ > this->total_page_)
{
return comp_book;
}
else
{
return *this;
}
}

 

-> 객체를 참조로 넘겨주는 장점? 복사할 필요가 없어진다.

 

 

3. public, private, protected 접근 제어 지시자

 

- 정보 은닉

 

C++에서 구조체의 모든 멤버는 외부에서 언제나 접근할 수 있다

 

하지만 클래스는 객체 지향 프로그래밍의 기본 규칙 중 하나인 정보 은닉에 대해서도 생각해야 한다.

 

정보 은닉이란 사용자가 굳이 알 필요가 없는 정보는 사용자로부터 숨겨야 한다는 개념이다.

 

결과적으로 사용자는 언제나 최소한의 정보만으로 프로그램을 손쉽게 사용할 수 있게 한다.

 

- 접근 제어 (access control)

 

C++에서는 이러한 정보 은닉을 위해 접근 제어라는 기능을 제공한다

 

접근 제어란 접근 제어 지시자를 사용해 클래스 외부에서 직접적인 접근을 허용하지 않는 멤버들을 설정할 수 있도록 하여, 정보 은닉을 구체화 하는 것이다.

 

- public

 

public 접근 제어 지시자를 사용하여 선언된 클래스 멤버는 외부로 공개되며 해당 객체를 사용하는 프로그램 어디에서나 직접 접근할 수 있습니다

 

public 멤버 함수는 해당 객체의 private 멤버와 프로그램 사이의 인터페이스 역할을 한다.

 

프로그램은 이렇나 pulblic 멤버 함수를 통해 해당 객체의 private 멤버에도 접근 가능하다.

 

- private

 

private 접근 제어 지시자를 사용하여 선언된 클래스 멤버는 외부에 공개되지 않고, 외부에서 직접 접근할 수도 없다.

 

프로그램은 private 멤버에 직접 접근할 수 없으며, 해당 객체의 public 멤버 함수를 통해서만 접근할 수 있다.

 

클래스의 기본 접근 제어 권한이기 때문에 클래스 선언 시 private 접근 제어 지시자는 생략할 수 있따.

 

- protected

 

C++ 클래스는 private 멤버로 정보를 은닉하고, public 멤버로 사용자나 프로그램과의 인터페이스를 구축한다.

 

여기에 파생 클래스와 관련된 접근 제어 지시자가 하나 더 존재한다.

 

protected 멤버는 파생 클래스에 대해서는 public 멤버처럼 취급받고, 외부에서는 private 멤버처럼 취급된다.

 

protected 멤버에 접근할 수 있는 영역은 다음과 같다.

 

1) 이 멤버를 선언한 클래스의 멤버 함수

2) 이 멤버를 선언한 클래스의 프렌드

3) 이 멤버를 선언한 클래스에서 public 또는 protected 접근 제어로 파생된 클래스

 

 

 

 

반응형