728x90
728x90

01 const

const 객체
const int num=10; //변수의 상수화
const SoSimple sim(20); //객체의 상수화

변수를 상수화하듯 객체도 상수화 할 수 있다.
객체에 const 선언이 붙으면 이 객체의 대상으로 const 멤버함수만 호출이 가능하다.
객체의 데이터 변경을 허용하지 않기 때문이다.  아예 호출부터 허용을 막아 안전하게 코드를 구성하는 것.

const 객체의 특성 확인 예제
#include <iostream>
using namespace std;

class SoSimple
{
private:
    int num;
public:
    SoSimple(int n):num(n)
    {   }
    SoSimple& AddNum(int n){
        num+=n;
        return *this;
    }
    void ShowData() const { //내부에서 출력만 할 경우 무조건 const
        cout<<"num: "<<num<<endl;
    }
};

int main()
{
    const SoSimple obj(7); //const 객체를 생성
    // obj.AddNum(20); // const 함수가 아니어서 호출이 불가
    obj.ShowData(); //const 함수여서 const 객체를 대상으로 호출이 가능

    return 0;
}

멤버변수에 저장된 값을 수정하지 않는 함수는 가급적 const로 선언해서 const 객체에서도 호출이 가능하도록 한다.

 

const와 함수 오버로딩

오버로딩 조건은 매개변수의 수나 자료형이 다른 것 + const의 선언유무

void SimpleFunc() { ... }
void SimpleFunc() const { ... }

 

오버로딩된 const 함수가 호출되는 예

#include <iostream>
using namespace std;

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

void YourFunc(const SoSimple &obj){
    // 전달 인자를 const 참조자로 받는다
    // 참조자를 이용한 함수호출의 결과로 const 멤버함수가 호출된다
    obj.SimpleFunc();
}

int main()
{
    SoSimple obj1(2);
    const SoSimple obj2(7);
    
    obj1.SimpleFunc();
    obj2.SimpleFunc();
    
    YourFunc(obj1);
    YourFunc(obj2);
    
    return 0;
}

 

 

 

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

클래스의 friend 선언

A클래스가 B클래스를 대상으로 friend 선언을 하면 B클래스는 A클래스의 private 멤버에 직접 접근이 가능하며
그 반대도 전제조건에 접근하려는 클래스를 대상으로 friend 선언을 해줘야 된다. 

friend 선언은 클래스 내 어디든 있을 수 있다 - private, public 영역 어디든..

#include <iostream>
#include <cstring>
using namespace std;

class 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(const char* num){
        strcpy(phNum, num);
    }
    void ShowYourFriendInfo(Boy &frn);
    friend class Boy; //Boy클래스에 대한 friend 선언
};

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

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

 

* 단점 : 정보은닉을 무너뜨리는 문법이다. 오로지 연산자 오버로딩 시 사용한다.

 

함수의 friend 선언 

전역 함수를 대상으로도, 클래스의 멤버함수를 대상으로도 friend 선언이 가능하다.
자신이 선언된 클래스의 private 영역에 접근이 가능하다.

#include <iostream>
#include <cstring>
using namespace std;

class Point;

class PointOP
{
private:
    int opcnt;
public:
    PointOP(): opcnt(0)
    {   }
    
    Point PointAdd(const Point&, const Point&);
    Point PointSub(const Point&, const Point&);
    ~PointOP(){
        cout<<"Operation times: "<<opcnt<<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); //friend선언으로 private멤버에 접근이 가능
}

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)
{
    cout<<"x: "<<pos.x<<", ";
    cout<<"y: "<<pos.y<<endl;
}

 

 

 

 

03. C++에서의 static

C++에서는 멤버변수와 멤버함수에 static 선언을 추가할 수 있다.

* C언어 복습
전역변수에 선언된 static의 의미 : 선언된 파일 내에서만 참조를 허용하겠다는 의미
함수 내에 선언된 static의 의미 : 한번만 초기화되고 지역변수와 달리 함수를 빠져나가도 소멸되지 않는다

static변수 : visible한 범위는 블록 내이지만 메모리는 프로그램이 종료가 될 때 까지 해제가 되지 않는다
static함수 : https://dojang.io/mod/page/view.php?id=691

 

#include <iostream> 
using namespace std;

void Counter()
{
    static int cnt; //초기화하지 않으면 0으로 초기화되며 딱 한번 실행된다
    cnt++;
    cout<<"Current cnt: "<<cnt<<endl;
}

int main(void){
    for(int i=0; i<10; i++){
        Counter();
    }
    return 0;
}

 

 

전역변수가 필요한 상황
#include <iostream>
using namespace std;

int simObjCnt=0;
int cmxObjCnt=0;

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

int main()
{
    SoSimple sim1;
    SoSimple sim2;
    
    SoComplex com1;
    SoComplex com2=com1;
    SoComplex();
    return 0;
}

 

전역변수는 어디에서든 접근이 가능하여 문제를 일으킬 소지가 있다. simObjCnt와 cmxObjCnt를 SoSimple, SoCimplex 클래스의 static 멤버로 두면 문제 발생을 예방할 수 있다.

 

static멤버변수 (클래스 변수)

일반 멤버변수와 다르게 클래스당 하나씩만 생성된다.
일반 지역변수처럼 객체가 생성될 때마다 함께 생성되어 객체별로 유지되는 변수가 아니다.
객체 생성여부와 별개로 메모리 공간에 하나만 할당되어 공유되는 변수

위의 코드를 static 변수로 바꾸었다.

#include <iostream>
using namespace std;

class SoSimple{
private:
    static int simObjCnt; //클래스 변수
public:
    SoSimple(){
        simObjCnt++;
        cout<<simObjCnt<<"번째 SoSimple 객체"<<endl;
    }
 };
 
class SoComplex
{
private:
    static int cmxObjCnt; //클래스 변수
public:
    SoComplex(){
        cmxObjCnt++;
        cout<<cmxObjCnt<<"번째 SoComplex 객체"<<endl;
    }
    SoComplex(SoComplex &copy){
        cmxObjCnt++;
        cout<<cmxObjCnt<<"번째 SoComplex 객체"<<endl;
    }
};

int SoSimple::simObjCnt=0; //static 멤버변수의 초기화
int SoComplex::cmxObjCnt=0; //static 멤버변수의 초기화

int main()
{
    SoSimple sim1;
    SoSimple sim2;
    
    SoComplex com1;
    SoComplex com2=com1;
    SoComplex();
    return 0;
}

 

static 멤버변수가 private 선언되면 클래스 내부에서만 접근되고, public으로 선언되면 클래스의 이름이나 객체의 이름을 통해서 어디든 접근 가능하지만, privte으로 유지하고 싶다면 이렇게 처리하면 밖에서도 쓸 수 있다. (외부 출처)

....
	int GetPrivateVal(){
		return simObjCnt;
	}
}; //클래스 내부

 

static 멤버변수의 초기화 문법
int SoSimple::simObjCnt=0;
// SoSimple클래스의 멤버변수simObjCnt가 메모리공간에 저장될 때 0으로 초기화해라

 

static 변수를 생성자에서 초기화하면 안 되는 이유

객체가 생성될때마다 0으로 초기화된다
객체가 생성될 때 동시에 생성되는 변수가 아니고 이미 메모리 공간에 할당이 이뤄진 변수다

 

static 멤버변수는 어디서든 접근이 가능하기 때문에, 클래스 내부에서 private 선언되면 해당 클래스 객체만 접근이 가능하지만 public 선언되면 클래스 이름/객체의 이름을 통해 어디서든 접근이 가능하다.
아래 두 문장은 sim1 sim2의 멤버변수에 접근하는 듯한 혼동 여지가 있어 첫번째 문장처럼 클래스의 이름을 사용해서 접근하는 것을 추천한다. 

SoSimple::simObjCont;

// 헷갈린다
sim1.simObjCnt;
sim2.simObjCnt;

 

static 멤버함수

선언된 클래스의 모든 객체가 공유한다
public으로 선언되면 클래스 이름을 사용해 호출이 가능하다
객체의 멤버로 존재하는 것이 아니다 

 

컴파일 에러 발생 예

class SoSimple{
private:
    int num1;
    static int num2;
public:
    SoSimple(int n): num1(n)
    {  }
    static void Adder(int n)
    {
        num1+=n; //error
        num2+=n;
    }
 };
 int SoSimple::num2=0;

객체의 멤버가 아니니까 멤버변수에 접근이 안된다. 
객체생성 이전에도 호출이 가능하다 (이미 메모리공간에 할당됐다) ..멤버변수는 아직 안만들어졌을 것이다

static 멤버함수 내에서는 static 멤버변수와 static 멤버함수만 호출이 가능하다

전역함수를 대체해서 쓰인다.

 

const static 멤버

클래스 내에 선언된 const 멤버변수(상수)의 초기화는 이니셜라이저를 통해야만 한다.
const static으로 선언되는 멤버변수(상수)는 선언과 동시에 초기화가 가능하다.
const static 멤버변수는 클래스가 정의될 때 지정된 값이 유지된다
그래서 초기화가 가능하게 문법으로 정의하고 있다.

#include <iostream>
using namespace std;
class CountryArea
{
public:
    const static int RUSSIA = 1707540;
    const static int CANADA = 998467;
    const static int CHINA = 957290;
    const static int SOUTH_KOREA = 9922;
    // const static 상수는 하나의 클래스에 둘 이상 모이는 게 일반적
};

int main(void)
{
 cout<<"러시아 면적: "<<CountryArea::RUSSIA<<"km"<<endl;
 cout<<"캐나다 면적: "<<CountryArea::CANADA<<"km"<<endl;
 cout<<"중국 면적: "<<CountryArea::CHINA<<"km"<<endl;
 cout<<"한국 면적: "<<CountryArea::SOUTH_KOREA<<"km"<<endl;
 // 클래스의 이름을 통해 접근하는게 편할 뿐만 아니라
 // 점근하는 대상에 대한 정보를 잘 담는다.
}

 

 

mutable

const 함수 내에서의 값의 변경을 예외적으로 허용하는 키워드이다.
mutable의 과도한 사용은 const 선언의 의미를 퇴색시키기에 가급적 사용 빈도를 낮춰야 한다.

#include <iostream>
using namespace std;

class SoSimple
{
private:
    int num1;
    mutable int num2; //const 함수에 대해 예외 - const 함수 내에서의 변경이 허용된다
public:
    SoSimple(int n1, int n2)
        :num1(n1), num2(n2)
    { }
    void ShowSimpleData() const
    {
        cout<<num1<<", "<<num2<<endl;
    }
    void CopyToNum2() const
    {
        num2=num1;
    }
};

int main(void)
{
    SoSimple sm(1, 2);
    sm.ShowSimpleData();
    sm.CopyToNum2();
    sm.ShowSimpleData();
    return 0;
}

728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

v