5 분 소요

const와 관련해서 아직 못다한 이야기

const 객체와 const 객체의 특성들

#include <iostream>

class SoSimple
{
private:
    int num;

public:
    SoSimple(int n) : num(n)
    {
    }
    SoSimple &AddNum(int n)
    {
        num += n;
        return *this;
    }
    void ShowData() const
    {
        std::cout << "num: " << num << std::endl;
    }
};

int main()
{
    const SoSimple obj(7);
    obj.ShowData();
    return 0;
}

>>>
num: 7

const 객체는 const 함수만 호출을 할 수 있다.

const 와 함수 오버로딩

const 객체는 const 함수만 사용이 가능하지만 일반 함수와 const 함수는 서로 오버로딩이 가능하다. (받을 수 있는 매개 변수가 같더라도 다른 기능을 하므로! )

#include <iostream>

class SoSimple
{
private:
    int num;

public:
    SoSimple(int n) : num(n)
    {
    }
    SoSimple &AddNum(int n)
    {
        num += n;
        return *this;
    }
    void SimpleFunc()
    {
        std::cout << "SimpleFunc: " << num << std::endl;
    }
    void SimpleFunc() const
    {
        std::cout << "const SimpleFunc: " << num << std::endl;
    }
};

void YourFunc(const SoSimple &obj)
{
    obj.SimpleFunc();
}

int main()
{
    SoSimple obj1(2);
    const SoSimple obj2(7);

    obj1.SimpleFunc();
    obj2.SimpleFunc();

    YourFunc(obj1);
    YourFunc(obj2);
    return 0;
}

>>>
SimpleFunc: 2
const SimpleFunc: 7
const SimpleFunc: 2
const SimpleFunc: 7

위 예시를 보면 SimpleFunc는 오버 로딩이 된 상태이다. 일반 객체인 경우는 일단 SimpleFunc()를 호출하여서 SimpleFunc: 2 가 출력이 되지만 const가 붙은 객체인 경우에는 const 함수를 호출하여서 const SimpleFunc: 가 출력이 되는 것이다.


obj1 같은 경우는 일반 객체지만 YourFunc를 통해서 결국 obj 가 const 객체이기 때문에 그 obj의 SimpleFunc 는 const 함수인 것이다.

클래스와 함수에 대한 friend 선언

넌 오늘부터 내 친구야 ! ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ


라는 뜻,,,,

클래스의 friend 선언

A 클래스가 B 클래스를 대상으로 friend 선언 => B 클래스는 A 클래스의 private 멤버에 직접 접근 가능

반대로 B 클래스가 A 클래스를 대상으로 friend 선언 => A 클래스는 B 클래스의 private 멤버에 직접 접근 가능

#include <iostream>
#include <cstring>

class Girl; // Girl이라는 이름이 클래스의 이름임을 알림

class Boy
{
private:
    int height;
    friend class Girl;

public:
    Boy(int len) : height(len)
    {
    }
    void ShowYourFriendInfo(Girl &frn);
};

class Girl
{
private:
    char phNum[20];

public:
    Girl(char *num)
    {
        strcpy(phNum, num);
    }
    void ShowYourFriendInfo(Boy &frn);
    friend class Boy;
};

void Boy::ShowYourFriendInfo(Girl &frn)
{
    std::cout << "Her phone number: " << frn.phNum << std::endl;
}

void Girl::ShowYourFriendInfo(Boy &frn)
{
    std::cout << "His height: " << frn.height << std::endl;
}

int main()
{
    Boy boy(170);
    Girl girl("010-3809-7668");
    boy.ShowYourFriendInfo(girl);
    girl.ShowYourFriendInfo(boy);
    return 0;
}

>>>
Her phone number: 010-3809-7668
His height: 170

private이나 public 둘다 friend 선언이 가능하다. 그리고 Boy 클래스에서 나오는 Girl 이라는 것이 클래스임을 미리 알려야 한다.

출력 결과는 보는 것 처럼 private 멤버 변수임에도 클래스끼리 접근이 가능한 것을 확인할 수 있다.

friend 선언은 언제?

friend 선언은 정보 은닉을 무너뜨리는 문법이다. 딱히 특정 부분에 사용한다를 말할 수는 없지만 friend를 남용하게 된다면 위험할 수 있다.

함수의 friend 선언

#include <iostream>

class Point;

class PointOP
{
private:
    int opcnt;

public:
    PointOP() : opcnt(0)
    {
    }
    Point PointAdd(const Point &, const Point &);
    Point PointSub(const Point &, const Point &);
    ~PointOP()
    {
        std::cout << "Operation times: " << opcnt << std::endl;
    }
};

class Point
{
private:
    int x;
    int y;

public:
    Point(const int &xpos, const int &ypos) : x(xpos), y(ypos)
    {
    }
    friend Point PointOP::PointAdd(const Point &, const Point &);
    friend Point PointOP::PointSub(const Point &, const Point &);
    friend void ShowPointPos(const Point &);
};

Point PointOP::PointAdd(const Point &pnt1, const Point &pnt2)
{
    opcnt++;
    return Point(pnt1.x + pnt2.x, pnt1.y + pnt2.y);
}

Point PointOP::PointSub(const Point &pnt1, const Point &pnt2)
{
    opcnt++;
    return Point(pnt1.x - pnt2.x, pnt1.y - pnt2.y);
}

int main()
{
    Point pos1(1, 2);
    Point pos2(2, 4);
    PointOP op;

    ShowPointPos(op.PointAdd(pos1, pos2));
    ShowPointPos(op.PointSub(pos2, pos1));
    return 0;
}

void ShowPointPos(const Point &pos)
{
    std::cout << "x: " << pos.x << ", ";
    std::cout << "y: " << pos.y << std::endl;
}

>>>
x: 3, y: 6
x: 1, y: 2
Operation times: 2

C++에서의 static

#include <iostream>

int simObjCnt = 0;
int cmxObjCnt = 0;

class SoSimple
{
public:
    SoSimple()
    {
        simObjCnt++;
        std::cout << simObjCnt << "번째 SoSimple 객체" << std::endl;
    }
};

class SoComplex
{
public:
    SoComplex()
    {
        cmxObjCnt++;
        std::cout << cmxObjCnt << "번째 SoComplex 객체" << std::endl;
    }
    SoComplex(SoComplex &copy)
    {
        cmxObjCnt++;
        std::cout << cmxObjCnt << "번째 SoComplex 객체" << std::endl;
    }
};

int main()
{
    SoSimple sim1;
    SoSimple sim2;

    SoComplex com1;
    SoComplex com2 = com1;
    SoComplex();

    return 0;
}

>>>
1번째 SoSimple 객체
2번째 SoSimple 객체
1번째 SoComplex 객체
2번째 SoComplex 객체
3번째 SoComplex 객체

예시를 보면 simObjCnt 는 SoSimple 클래스에서만 공유하는 변수로 사용되고 있고 cmxObjCnt 는 SoComplex 클래스에서만 공유하는 변수로 사용되고 있다. 하지만 각 클래스에서만 사용되도록 하는 것이 아닌 전역 변수로 사용되고 있으므로 문제를 일으킬 소지가 매우 높다.

static 멤버 변수

static 멤버 변수는 클래스 변수라고도 한다.

#include <iostream>

class SoSimple
{
private:
    static int simObjCnt; //🌟

public:
    SoSimple()
    {
        simObjCnt++;
        std::cout << simObjCnt << "번째 SoSimple 객체" << std::endl;
    }
};
int SoSimple::simObjCnt = 0; //🌟

class SoComplex
{
private:
    static int cmxObjCnt; //🌟

public:
    SoComplex()
    {
        cmxObjCnt++;
        std::cout << cmxObjCnt << "번째 SoComplex 객체" << std::endl;
    }
    SoComplex(SoComplex &copy)
    {
        cmxObjCnt++;
        std::cout << cmxObjCnt << "번째 SoComplex 객체" << std::endl;
    }
};
int SoComplex::cmxObjCnt = 0; //🌟

int main()
{
    SoSimple sim1;
    SoSimple sim2;

    SoComplex com1;
    SoComplex com2 = com1;
    SoComplex();

    return 0;
}

>>>
1번째 SoSimple 객체
2번째 SoSimple 객체
1번째 SoComplex 객체
2번째 SoComplex 객체
3번째 SoComplex 객체

static 멤버 변수의 또 다른 접근 방법

static 멤버 변수를 public 으로 두어서 결국 어디서든 접근이 가능한 변수를 만들 수 있다.

#include <iostream>

class SoSimple
{
public:
    static int simObjCnt;

public:
    SoSimple()
    {
        simObjCnt++;
    }
};
int SoSimple::simObjCnt = 0;

int main()
{
    std::cout << SoSimple::simObjCnt << "번째 SoSimple 객체" << std::endl;
    SoSimple sim1;
    SoSimple sim2;

    std::cout << SoSimple::simObjCnt << "번째 SoSimple 객체" << std::endl;
    std::cout << sim1.simObjCnt << "번째 SoSimple 객체" << std::endl;
    std::cout << sim2.simObjCnt << "번째 SoSimple 객체" << std::endl;

    return 0;
}

>>>
0번째 SoSimple 객체
2번째 SoSimple 객체
2번째 SoSimple 객체
2번째 SoSimple 객체

static 변수를 public 으로 권한을 주었기 때문에 simObjCnt를 main 에서도 접근이 가능함을 알 수 있다. 이를 통해서 main 에서도 몇 번째 객체인지를 출력할 수 있다.

static 멤버 함수

  • 선언된 클래스의 모든 객체가 공유한다.
  • public으로 선언이 되면, 클래스의 이름을 이용해서 호출이 가능하다.
  • 객체의 멤버로 존재하는 것이 아니다.
  • static 멤버 함수 내에서는 static 멤버 변수와 static 멤버 함수만 호출이 가능하다.

const static 멤버

const static 멤버 변수는 선언과 동시에 초기화가 가능하다.

키워드 mutable

const 함수 내에서의 값의 변경을 예외적으로 허용한다라는 의미를 가지고 있다.

#include <iostream>

class SoSimple
{
private:
    int num1;
    mutable int num2;

public:
    SoSimple(int n1, int n2) : num1(n1), num2(n2)
    {
    }
    void ShowSimpleData() const
    {
        std::cout << num1 << ", " << num2 << std::endl;
    }
    void CopyToNum2() const
    {
        num2 = num1;
    }
};

int main()
{
    SoSimple sm(1, 2);
    sm.ShowSimpleData();
    sm.CopyToNum2();
    sm.ShowSimpleData();

    return 0;
}

>>>
1, 2
1, 1

num2 가 mutable 이므로 const 함수임에도 불구하고 CopyToNum2 에서 num2가 바뀔 수 있다. 하지만 적어도 num1 은 변할 수 없다.

댓글남기기