leesangwon0114

I am Research Engineer. Currently working in KT.

C++Intermediate 4. C++기본문법_const member function

07 Oct 2018 » c++

codenuri 강석민 강사 강의 내용기반으로 정리한 내용입니다.


상수 멤버 함수의 필요성

#include <iostream>
using namespace std;

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

    void set(int a, int b)
    {
        x = a;
        y = b;
    }
    void print() const
    {
        // x = 10; // error. 모든 멤버를상수 취급한다.
        cout << x << ", " << y << endl;
    }

};

int main()
{
    const Point p(1, 1);

    p.x = 10; // error p가 상수니까 안됨
    p.set(10, 20); // error x, y 가 바뀌면 안됨
    p.print(); // ?? 내부적으로 값을 바꾸지 않으니까 가능?
}

멤버함수 뒤에 const 를 붙이면 상수 멤버함수 - 안에서 모든 멤버를상수 취급

일반적으로 위의 Point 를 header 와 cpp를 나누어 사용

이때 main에서는 header 만 include 해 안에 내부 내용을 몰라 컴파일러는 p.print() error 냄

호출되게 하려면 print()를 반드시 상수멤버함수가 되어야 함

핵심 : 상수 객체는 상수 멤버함수만 호출할 수 있다.


struct Rect
{
    int ox, oy, width, height;

    Rect(int x = 0, int y = 0, int w = 0, int h =0) : ox(x), oy(y), width(w), height(h) {}

    int getArea() { return width * height; }
};

void foo( const Rect& r ) // call by value 보다는 const & 가 좋다.
{
    int n = r.getArea(); // error. - 상수 참조로 받으면 get을 못쓰는 현상...
}

int main()
{
    Rect r(0, 0, 10, 10);

    int n = r.getArea(); // ok.

    foo(r);
}

Rect struct 의 get Area() const 붙이면 error 해결됨

상수함수는 선택이 아니라 필수다!

객체의 상태를 변경하지 않은 모든 멤버함수는(get***) 반드시 const 멤버함수가 되어야 한다.


논리적 상수성

class Point
{
    int x, y;

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

    const char* toString() const
    {
        static char s[16];
        sprintf(s, "%d, %d", x, y);
        return s;
    }

};

int main()
{
    Point p1(1, 1);
    cout << p1.toString() << endl;
    cout << p1.toString() << endl;

    cont Point p2(2, 2);
    cout << p2.toString() << endl; // toString 이 x, y 변경안해도 허용안됨 -> toString 뒤에 const 붙여야함
}
class Point
{
    int x, y;
    char cache[16];
    bool cache_valid = false;
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {}

    const char* toString() const
    {
        if (cahce_valid == false) {
            sprintf(cache, "%d, %d", x, y);
            cache_valid = true; // 상수함수라 여기서 변경 불가
        }
        return cache;
    }

};

int main()
{
    Point p1(1, 1);
    cout << p1.toString() << endl;
    cout << p1.toString() << endl; // 두번 출력 시 cache 되는 기능 추가
}

외부에서 바라봤을 때 상태를 변경하지 않아 toString 이 상수가 되는 것이 맞지만 내부적으로 테크닉을 쓰다보니 값을 변경해야 할 때가 있음

해결책 1. char cache 앞에 mutable char 로 변경 -> mutable 멤버변수: 상수 멤버함수 안에서도 값을 변경 가능.

class Point
{
    int x, y;
    mutable char cache[16];
    mutable bool cache_valid = false;
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {}

    const char* toString() const
    {
        if (cahce_valid == false) {
            sprintf(cache, "%d, %d", x, y);
            cache_valid = true; // 상수함수이지만 mutable이라 변경 가능
        }
        return cache;
    }

};

int main()
{
    Point p1(1, 1);
    cout << p1.toString() << endl;
    cout << p1.toString() << endl; // 두번 출력 시 cache 되는 기능 추가
}

해결책 2. x, y는 상수멤버함수에서 변하지 않는 멤버이고 cache, cache_valid는 상수멤버함수에서 변하는 멤버임 -> 분리하면됨

struct Cache
{
    char cache[16];
    bool cache_valid = false;
}
class Point
{
    int x, y;
    Cache* pCache
public:
    Point(int a = 0, int b = 0) : x(a), y(b) {
        pCache = new Cache;
    }

    const char* toString() const
    {
        if (pCache->cahce_valid == false) {
            sprintf(pCache->cache, "%d, %d", x, y);
            pCache->cache_valid = true; // 상수함수이지만 mutable이라 변경 가능
        }
        return pCache->cache;
    }
    ~point() {delete pCache;}
};

int main()
{
    Point p1(1, 1);
    cout << p1.toString() << endl;
    cout << p1.toString() << endl;
}

상수 멤버 함수 참고 사항

struct Test
{
    void foo() { cout << "foo()" << endl; } // 1
    void foo() const { cout << "foo() const" << endl; } // 2

    void goo() const;
};

void Test::goo() const // 구현에도 반드시 const 붙여야 함
{

}

int main()
{
    Test t1;
    t1.foo(); // 1번, 없으면 2번

    const Test t2;
    t2.foo(); // 2번, 없으면 error
}