반응형

static member data

  • static 멤버 변수 모양

    • 클래스 안에 선언(declaration) -> static 키워드 사용

    • 클래스 외부에 정의(definition) -> static 키워드 사용 안함

  • static 멤버 변수

    • 모든 객체가 공유(전역 변수와 유사)

    • 접근 지정자 사용 가능

    • 객체를 생성하지 않아도 메모리에 존재(전역)
    • 객체 생성시 static 멤버는 객체 메모리에 불포함(sizeof 객체로 해보면 static 멤버 제외 사이즈가 리턴)
    • "클래스 이름::변수 이름" 사용으로 클래스별 동일 이름 사용 가능
  • static 멤버 접근 방법
    • 클래스이름::멤버이름 : Car::cnt // 권장
    • 객체이름::멤버이름 : c1.cnt // 일반 멤버인지 static 멤버인지 구분 모호
class Car
{
    int speed;
public:
    static int cnt; // 선언(Declaration)
    Car() { ++cnt; }
    ~Car() { --cnt; }
};
int Car::cnt = 0; // 정의(Definition)

int main()
{
    // 1. 객체 없이도 메모리에 존재
    std::cout << Car::cnt << std::endl;

    // 2. 객체 메모리에 포함되지 않음
    Car c1, c2;
    
    std::cout << Car::cnt << std::endl; // 3. 접근 방법(클래스::멤버이름)
    std::cout << c1.cnt << std::endl; // 3. 접근 방법(객체.멤버이름)
}

static member function

  • static 멤버 함수

    • 객체 없이 호출 가능한 멤버 함수

    • 클래스이름::함수이름()으로 호출 가능

    • 객체이름.함수이름()으로도 호출 가능

    • 일반 멤버 함수 : 호출하려면 반드시 객체가 필요

class Car
{
    int speed;
public:
    static int cnt; // 선언(Declaration)
    Car() { ++cnt; }
    ~Car() { --cnt; }

    static int getCount() { return cnt; }
};
int Car::cnt = 0; // 정의(Definition)

int main()
{
    // 객체 생성과 무관하게 호출 가능(클래스::함수이름)
    std::cout << Car::getCount() << std::endl;

    Car c1, c2;
   
    // 객체를 통해서도 호출 가능(객체.함수이름)
    std::cout << c1.getCount() << std::endl;
}
  • static 멤버 함수 특징

    • 일반 멤버 data에 접근 불가

    • static 멤버 데이터만 접근 가능

    • 선언과 구현으로 분리할때 선언에만 static 표기

class Test
{
    int data1;
    static int data2; // 선언부에는 static 표기
public:
    static void f2()
    {
        data1 = 0; // 일반 멤버 data1에 접근 불가(컴파일 에러)
        data2 = 0; // static 멤버 data2에 접근 가능
    }
    static void f3(); // 선언부에는 static 표기
};

void Test::f3()  // 구현부 분리시 static 표기 안함
{

}

const member function

  • 상수 멤버 함수(const member function) 특징

    • 상수 멤버 함수 안에서는 모든 멤버를 상수 취급

    • 멤버의 값을 변경할 수 없음

    • 상수 멤버 함수 안에서는 상수 멤버 함수만 호출 가능

    • 상수 멤버 함수와 비상수 멤버 함수를 동일한 이름으로  제공 가능
class Point
{
public:
    int x, y;
    Point(int a, int b) : x(a), y(b) {}

    void set(int a, int b)
    {
        x = a;
        y = b;
    }
    
    void getX() const { return x; }
    void getY() const { return y; }

    void print() const // 상수 멤버 함수
    {
        getX() // const 멤버 함수 호출(OK)
        x = 10; // 멤버 데이터 변경(Error)
        set(1, 2); // 일반 멤버 함수 호출(Error)
        std::cout << x << ", " << y << std::endl; // 멤버 데이터 읽기(OK)
    }
};

int main()
{
    Point p(1, 2);
    p.x = 10;
    p.set(10, 10);
    p.print();
}
  • 상수 멤버 함수(const member function) 필요성

    • 상수 객체는 상수 멤버 함수만 호출할 수 있음

    • 객체의 상태를 변경하지 않는 모든 멤버 함수는 상수 함수가 되어야 함

    • getter 대부분 상수 멤버 함수가 되어야 함

class Point
{
public:
    int x, y;
    Point(int a, int b) : x(a), y(b) {}

    void set(int a, int b)
    {
        x = a;
        y = b;
    }

    void print() const; // 구현부를 분리시 선언, 구현부 모두 const 표시
};

void Point::print() const // 구현부를 분리시 선언, 구현부 모두 const 표시
{
    std::cout << x << ", " << y << std::endl;
}

// 자주쓰는 패턴
void foo(const Point& p)
{
    // p의 멤버함수가 상수 멤버함수로 제공되지 않는다면 호출 가능한 멤버가 없음
}

int main()
{
    const Point p(1, 2);
    p.print();  // 상수객체는 상수 멤버 함수만 호출 가능(const가 아닐경우 error)
}
  • mutable
    • 상수 멤버 함수 안에서 값을 변경 하고 싶을때
    • 논리적으로는 상수 멤버 함수지만 다양한 이유로 멤버의 값을 변경하고 싶을때
class Point
{
public:
    int x, y;
    int data = 0;
    mutable int fooCount = 0;

    const int* getPointer() const { return &data; }

    void foo() // 동일 이름의 상수 멤버 함수, 비상수 멤버 함수를 동시에 제공 가능
    {
        std::cout << "foo()" << std::endl;
    }
    void foo() const // 동일 이름의 상수 멤버 함수, 비상수 멤버 함수를 동시에 제공 가능
    {
        fooCount++; // 상수 멤버 함수에서 특별한 이유로 값 변경 필요시 mutable 지시자 선언
        std::cout << "foo() const" << std::endl;
    }
};


int main()
{
    Point p1;
    p1.foo(); // 1. 비상수 함수 호출, 2. 없을 경우 상수 함수 호출
}

this pointer

  • 내부 원리

    • 멤버 함수 호출 시 내부적으로 객체의 주소가 this라는 이름으로 전달됨

class Point
{
    int x = 0;
    int y = 0;
public:
    void set(int a, int b)
    {
        std::cout << this << std::endl; // main의 p1, p2 객체 주소랑 this는 동일 값
    }
};

int main()
{
    Point p1;
    Point p2;

    std::cout << &p1 << std::endl;
    p1.set(10, 20); // 내부적으로는 멤버 함수는 호출 시 객체의 주소가 추가로 전달됨

    std::cout << &p2 << std::endl;
    p2.set(10, 20); // 내부적으로는 멤버 함수는 호출 시 객체의 주소가 추가로 전달됨

}
  • 멤버의 이름과 지역변수(함수 인자)이름이 동일할때

    • 지역 변수가 우선시 됨

    • this를 사용하면 멤버 변수에 접근 가능

class Point
{
    int x = 0;
    int y = 0;
public:
    void set(int x, int y)
    {
        this->x = x; // 동일 변수명 사용시 지역변수 우선이지만 명시적인 구분을 위해 this 활용 가능
        this->y = y;
    }
};

int main()
{
    Point p1;
    p1.set(10, 20);
}
  • this를 리턴하는 함수

    • 멤버 함수 호출을 연속적으로 사용할수 있음

    • 자신(*this)을 리턴시 반드시 참조로 리턴 해야 함(return by reference 참조)

class Point
{
    int x = 0;
    int y = 0;
public:
    void set(int a, int b)
    {
        this->x = a;
        this->y = b;
    }

    Point* foo() { return this; } // this 이용 포인터 리턴
    Point& goo() { return *this; } // this 이용 값에 대한 레퍼런스 리턴
    //Point goo() { return *this; } // this 이용 값으로 바로 리턴시 임시 객체가 계속 복사됨 주의!
};

int main()
{
    Point p1;
    p1.foo()->foo()->foo(); // 포인터 재호출
    t.goo().goo().goo(); // 값 재호출
}
  • this 주의 사항
    • static 멤버 함수 안에서는 this를 사용 불가
    • static 멤버 함수 안에서는 this 사용이 불가하므로 멤버 데이터 접근이 불가능함
class Test
{
    int data;
public:
    static void foo()
    {
        std::cout << this << std::endl; // static 멤버 함수 내부에서는 this 사용 불가 Error
        data = 0; // this->data = 0; this 없어서 static 멤버 함수에서 일반 멤버 접근이 불가 Error
    }
};

int main()
{
    Test::foo(); // static 멤버 함수 호출(내부 this 객체 없음)
}
반응형

+ Recent posts