c++

20210722 객체 지향 프로그래밍 ~ static멤버함수,싱글톤

TIN9 2021. 7. 23.
반응형

안녕하세요 틴구입니다

다시 돌아왔어요 오늘도 이론에 대한 내용인데 진짜 처음 접하는 입장에서

엄청 어려운거 같아요 계속 읽어보고 타이핑해보고 해야겠어요

그럼 바로 들어가볼까요

객체지향 프로그래밍

객체지향 프로그래밍을 설명하기 앞서 절차지향 프로그래밍 부터 설명 드릴게요

절차지향 프로그래밍이란 c언어를 말하며 순차적으로 처리해주는 프로그래밍 기법입니다.

절차지향 프로그래밍은 컴퓨터와 유사하여 실행 속도가 빠르나 유지보수가 어렵습니다.

객체지향 프로그래밍이란 객체(모든 사물)를 제작하여 객체들을 조립하여 하나의 완성된

프로그램을 만들어나가는 방식을 말합니다.

C++에서는 객체지향 프로그래밍을 지원하기 위한 수단으로 클래스를 제공합니다.

객체지향 프로그래밍 같은 경우에 크게 4가지의 속성이 있습니다

객체지향 프로그래밍 속성

 

캡슐화 : 구조체와 같이 여러 데이터 혹은 함수를 묶어서 사용할 수 있게 만들어주는 속성입니다.

 

은닉화 : 구조체나 클래스의 멤버를 외부에 공개하는 수준을 설정할 수 있는 속성입니다.

그 수준은 크게 3가지로 나뉜다

 

public : 클래스 혹은 구조체의 내부(클래스나 구조체에 만들어져있는 함수 안)와 외부(클래스의 함수 안이 아닌 다른 클래스 혹은 다른 함수 안)에서 모두 접근이 가능한 속성입니다.

구조체는 아무것도 안 붙여줄경우 public이다.

 

private : 클래스 혹은 구조체의 내부에서만 접근이 가능하고 외부에서는 접근이 불가능합니다.

클래스는 아무것도 안 붙여줄경우 기본으로 private로 설정이 되어있습니다.

 

protected : 상속할때 설명 주신다고 합니다.

 

상속성 : 상속할때 설명을 해주신다고 합니다

다형성 : 이놈도 상속할때 설명해 주신다고 합니다.

 

클래스란 C++에서 객체지향을 위해 제공하는 수단입니다.

구조체와 마찬가지로 다양한 변수와 함수들을 클래스 내부에 만들어두고 사용이 가능합니다.

 

바로 public과 private를 예시로 보여드릴게요

 

class CPlayer

{

    int m_Number1;  // 클래스의 경우 아무것도 설정하지 않을경우 private이여서 Number1은 private에 속한다

public:   

    int m_Number2;  //  public:을 설정하고 그 밑에다가 변수를 선언했기 때문에 public에 속한다.

private:

    int m_Number3;  // private:을 설정하고 그 밑에다가 변수를 선언했기 때문에 private에 속한다.

}

 

int main()

{

    CPlayer Player = {};   // 여기서 class는 객체를 만들기 위한 수단이고 객체는 Player변수가 된다.

 

    Player.m_Number1 = 100;   // m_Number1의 경우 private에 속하기 때문에 변경이 안 된다.    X

    Player.m_Number2 = 200;   // m_Number2의 경우 public에 속하기 때문에 변경이 가능하다.   O

    Player.m_Number3 = 300;   // m_Number3의 경우 private에 속하기 때문에 변경이 안 된다.    X

 

    std::cout << 

 

    return 0;

}

 

생성자와 소멸자

생성자는 이 클래스를 이용해서 객체를 생성할때 자동으로 호출이 되는 함수이다.

소멸자는 이 클래스를 이용해서 생성한 객체가 메모리에서 해제될때 자동으로 호출이 되는 함수이다.

형태

class CPlayer

{

    int Number1;  

public:

    CPlayer()    // 생성자

    {

    }

 

    ~CPlayer()    // 소멸자

    {

    }

}

의 형태로 이루어져 있다

class CPlayer

{

    int Number1;  

public:

    CPlayer()    // 생성자

    {

        std::cout << "CPlayer 생성자" << std::endl;    

    }

 

    ~CPlayer()    // 소멸자

    {

        std::cout << "CPlayer 소멸자" << std::endl;

    }

}

int main()

{

    CPlayer Player;   // 이때 생성자가 생성이 된다

 

    return 0;   // 이때 소멸자가 생성이 된다.

}

 

이렇게 출력을 하면

CPlayer 생성자

CPlayer 소멸자

이렇게 출력이 된다.

위의 경우는 일반생성자 호출이고

아래의 방식처럼 활용할 수도 있다.

(구조체 생략)

int main()

{

    CPlayer* pPlayer = new CPlayer;      // new로 동적할당을 주어 생성자가 시작됨

 

    std::cout << "Test" << std::endl;      // Test출력

 

    delete pPlayer;                             // 메모리를 삭제하여 이때 소멸자가 생성이 된다.

 

    return 0;

}

또 Initializer를 사용하여 멤버 변수를 초기화할 수 있습니다

생성자 : 변수명(원하는타입의숫자)

CPlayer()    :

    m_Number1(100)

{

    Initializer를 사용하면

    int Num = 100;이랑 같은 말이다

    m_Number2 = 100;은

    int Num1;

    Num1 = 100;와 같은 형태이다.

}

 

 

생성자의 경우 오버로딩처럼 여러종류의 생성자를 만들어 놓고 사용할 수 있습니다.

class CPlayer
{
public:
    int     m_Number1;
    int     m_Number2;
    int     m_Number3;

 

    CPlayer()    :

       m_Number1(100)

   {

       std::cout << "m_Number1 : " << m_Number1 << std::endl;

   }

   CPlayer(int Number1)    :

       m_Number1(Number1)

   {

       std::cout << "m_Number1 : " << m_Number1 << std::endl;

   }

   CPlayer(int Number1, int Number2, int Number3)    :

       m_Number1(Number1)

       m_Number2(Number2)

       m_Number3(Number3)

   {

       std::cout << m_Number1 << "\t" << m_Number2 << "\t" << m_Number3 << std::endl;

   }

       ~CPlayer()
   {
      std::cout << "CPlayer 소멸자" << std::endl;
   }

}

int main()

{

   // 일반 생성자

   CPlayer Player1;

   // 인자 1개를 갖고있는 생성자

   CPlayer Player2(200);    // 인자int Number1에가 200의 값을 넣어주어 m_Number1 = 200;이 된다.

   // 인자 3개를 갖고있는 생성자

   CPlayer Player3(100, 200, 300);   // 인자 에다가 100, 200, 300순으로 넣어주게 된다.

 

   return 0;    // 생성자를 3번 출력하여 소멸자도 3번 출력이 된다.

}

를 출력하게 되면

m_Number1 : 100

m_Number1 : 200

100    200    300

CPlayer 소멸자

CPlayer 소멸자

CPlayer 소멸자

이 출력되게 된다.

클래스의 멤버함수

클래스의 멤버함수는 클래스의 멤버함수는 일반함수와 마찬가지로 함수 주소가 1개만 나오게 된다.
그런데 클래스의 멤버함수에서는 이 멤버함수를 호출한 객체의 멤버변수들의 값을 정확하게
인식하고 사용할 수 있습니다.
그 이유는 this 라는것 때문입니다.
this는 자기 자신에 대한 포인터이다.
this->m_Number1    이렇게 사용해야 하는데 this-> 는 생략할 수 있습니다.

this는 이 멤버함수를 호출할때 호출하는 객체로 지정이 되게 된다.
그렇기 때문에 매번 이 함수를 호출할때마다 this에는 호출하는 객체로 지정이 되는것이다.
매번 다른 객체로 호출한다면 다른 객체들로 매번 this가 바뀔것이다.

위의 코드에서

void Output()
    {
        std::cout << "Number1 : " << m_Number1 << std::endl;
    }

를 추가하고

int main()

{

 

    Player2.Output

    Player3.Output

 

    return 0;

}

을 추가하고 출력하면

Number1 : 200

Number1 : 100이 나오게 되는 개념입니다.

static 멤버함수

class CStatic
{
public:
    int     m_Number;
    static int  m_NumberStatic;
}
// 클래스의 static 멤버변수는 반드시 클래스 외부에 선언부분을 만들어주어야 한다.
// 기본으로 0으로 초기화가 되고 원하는 값을 대입해주어도 됩니다.
// 클래스의 static 멤버변수는 이 클래스를 이용해서 객체를 아무리 많이 생성하더라도
// 이 변수의 메모리는 딱 1개만 생성이 됩니다.
// 일반 멤버변수라면 객체의 수만큼 메모리 공간이 할당될 것입니다.
int CStatic::m_NumberStatic;

 

int main()
{
    CStatic std1;

    std1.m_NumberStatic = 100;
    CStatic::m_NumberStatic = 300;

    std::cout << std1.m_NumberStatic << std::endl;

 

    return 0;

}

위의 코드를 출력하면 300이 출력한다

std1.m_NumberStatic = 100;
CStatic::m_NumberStatic = 300;

이 두개의 코드는 똑같은 코드이다.

 

위에서 클래스 멤버함수에서는 this를 사용할 수 있었는데

static멤버함수에서는 this를 사용할 수 없습니다.

그렇기 때문에 일반 멤버변수는 사용할 수 없습니다.

여기에서는 static 멤버변수만 사용이 가능합니다.

함수포인터 형태가 전역함수와 동일한 형태로 사용이 가능합니다.

class CStatic
{
public:
    CStatic()
    {
    }

    ~CStatic()
    {
    }

public:
    int     m_Number;
    static int  m_NumberStatic;

void Output()
    {
        std::cout << "Output" << std::endl;
    }

 static void OutputStatic()
    {
        std::cout << "Output Static" << std::endl;
        //std::cout << m_Number << std::endl;  일반멤버변수 사용 불가
        //std::cout << this << std::endl;   this 사용불가
    }

}

// 클래스의 static 멤버변수는 반드시 클래스 외부에 선언부분을 만들어주어야 합니다
// 기본으로 0으로 초기화가 된다. 원하는 값을 대입해주어도 된다고 합니다
// 클래스의 static 멤버변수는 이 클래스를 이용해서 객체를 아무리 많이 생성하더라도
// 이 변수의 메모리는 딱 1개만 생성이 됩니다
// 일반 멤버변수라면 객체의 수만큼 메모리 공간이 할당될 것입니다.

int CStatic::m_NumberStatic;

 

int main()
{
    CStatic std1;

 

    return 0;

}

 

static 멤버함수 포인터 형태

void(*pFunc)() = CStatic::OutputStatic;

 

일반 멤버함수 포인터 형태

void(CStatic:: *pFunc1)() = &CStatic::Output;

 

// OutputStatic의 경우 this가 없기떄문에 그냥 호출하면 됨

pFunc();

 

// Output은 일반 멤버함수여서 반드시 this가 지정이 되어야 한다.

// 그렇기 때문에 어떤 객체를 this로 지정할지가 반드시 선행되어야 합니다.

int main()
{
    CStatic std1;

 

    (std1.*pFunc1)();

 

     return 0;

}

싱글톤

static함수를 이용해서 관리자 객체를 만들 수 있음

class CSingleton
{
    // 생성자와 소멸자가 private이라면 이 클래스의 외부에서 객체를 생성하는것이
    // 불가능하고 이 클래스의 외부에서 객체를 메모리에서 해제하는 것이 불가능하다.
private:
    CSingleton()
    {
    }

    ~CSingleton()
    {
    }

private:
    static CSingleton* m_pInst;

public:
    static CSingletonGetInst()
    {
        if (m_pInst == nullptr)
            m_pInst = new CSingleton;

        return m_pInst;
    }

    static void DestroyInst()
    {
        if (m_pInst != nullptr)
        {
            delete  m_pInst;
            m_pInst = nullptr;
        }
    }
};

CSingletonCSingleton::m_pInst = nullptr;

반응형

댓글