ref:http://blog.daum.net/coolprogramming/60



함수에 사용되는 const는 세 가지입니다.

const  int  Functionconst  int* arr ) const ;
  • 매개 변수 자료형에 사용되는 경우.
  • 함수 선언에 사용되는 경우.
  • 리턴 자료형에 사용되는 경우.

 

1, 매개 변수 자료형에 사용되는 const

 매개 변수 const를 공부하기 전에 선행 학습으로 in 파라미터(매개 변수)와 out 파라미터를 공부하도록 하겠습니다.

1.1, in, out 파라미터

파라미터의 사용 목적에 따라  in 파라미터와, out 파라미터로 나누어집니다.

  • in 파라미터는 함수에 데이터를 전달할 목적으로 사용됩니다. 호출자(caller)가 호출 받는자(callee)에게 데이터를 전달하기 위한 파라미터입니다. 즉 클라이언트에서 서버로 데이터를 전달하기 위해 사용하는 파라미터입니다.
  • out 파라미터는 함수에서 데이터를 받아올 목적으로 사용됩니다. 호출 받는자(callee)에서 호출자(caller)에게 데이터를 전달받기 위한 파라미터입니다. 즉 서버에서 클라이언트로 데이터를 전달하기 위해 사용하는 파라미터입니다.

 

in 파라미터 예제를 보겠습니다.

#include <iostream>
using namespace std;
void Print(int inParam)
{
    cout << inParam << endl;
}
void main( )
{
    int n = 10;
    Print( n );
}
  1. 10

 클라이언트 측(main()함수)에서 서버 측(Print()함수)으로 10이라는 데이터를 전달하고 있습니다.

 

 

 

C++언어에서 out 파라미터는 두 가지로 구현할 수 있습니다.

  • 포인터를 사용한 out 파라미터
  • 레퍼런스를 사용한 out 파라미터

 

첫째, 포인터 out 파라미터 예제입니다.

#include <iostream>
using namespace std;
void GetData(int* outParam)
{
    *outParam = 10;
}
void main( )
{
    int n = 0;
    GetData( &n );
    cout << n << endl;
}
  1. 10

 이 예제는 반대로 서버 측(main()함수)에서 클라이언트 측(Print()함수)으로 10이라는 데이터를 전달하고 있습니다.

 

 

 

둘째, 레퍼런스 out 파라미터 예제입니다.

#include <iostream>
using namespace std;
void GetData(int& outParam)
{
    outParam = 10;
}
void main( )
{
    int n = 0;
    GetData( n );
    cout << n << endl;
}
  1. 10

 이 예제도 반대로 서버 측(main()함수)에서 클라이언트 측(Print()함수)으로 10이라는 데이터를 전달하고 있습니다.

 

 

 

in 파라미터와 out 파라미터는 값 전달이냐? 참조 전달이냐? 로 나누어지는 것은 아닙니다.

파라미터의 사용 목적에 따라 나누어집니다.

  • in 파라미터로 값 전달, 주소 전달, 레퍼런스 전달 모두를 사용할 수 있습니다.
  • out 파라미터로 주소 전달과 페러런스 전달을 사용할 수 있습니다.

 

함수에서 데이터를 받아오는 방법은 두 가지가 있습니다.

  • out 파라미터 사용
  • 함수 반환(return) 사용

당연한 이야기죠?

 

1,2 매개 변수 자료형에 사용되는 const

 out 파라미터는 함수에서 데이터를 받아올(변경할) 목적으로 사용되므로 당연히 const를 붙이지 않습니다. 하지만 in 파라미터로 사용하는 포인터나 레퍼런스는 실수로 in 파라미터임에도 데이터가 수정되는 위험성을 갖게 됩니다. 이렇게 되면 서버에서 클라이언트 데이터를 변경하는 일이 발생합니다.

 

변수의 주소를 출력하기 위한 예제입니다. 

#include <iostream>
using namespace std;
//단지 n의 주소를 출력하기 위한 함수
void PrintAddress(int& inParam)
{
    cout << &inParam << endl;
    inParam = 100; // <= 이렇게 변경 위험성이 있음. 클라이언트 n이 변경됨
}
void main( )
{
    int n = 10;
    PrintAddress( n );
}
  1. 0012FF60

 n의 주소를 출력하기 위해 레퍼런스를 사용했습니다. 그러므로 이때의 inParam 변수는 in 파라미터입니다.

만약 inParam 변수가 어떤 이유에서 변경된다면 클라이언트의 n이 변경되는 것이므로 개발자는 무한 디버깅 모드로 시간을 낭비해야 합니다.

 

그래서 이와 같은 위험성을 사전에 방어하기 위하여 in 파라미터 포인터와 레퍼런스는 모두 const를 붙입니다.

만약 변경되면 컴파일러가 미리 알려주는 것입니다. 에러로~

이렇게..

#include <iostream>
using namespace std;
//단지 n의 주소를 출력하기 위한 함수
void PrintAddress(const int& outParam)
{
    cout << &outParam << endl;
    outParam = 100; // <= 에러~~ const는 변경할 수 없음
}
void main( )
{
    int n = 10;
    PrintAddress( n );
}

  포인터도 동일합니다. (포인터는 C언어의 const를 참고하세요.)

#include <iostream>
using namespace std;
//단지 n의 주소를 출력하기 위한 함수
void PrintAddress(const int* outParam)
{
    cout << outParam << endl;
    *outParam = 100; // <= 에러~~ const는 변경할 수 없음
}
void main( )
{
    int n = 10;
    PrintAddress( &n );
}

 간단하죠? 잉~

 

규칙은 간단합니다.

in 파라미터로 사용되는 포인터와 레퍼런스는 '이유불문' const를 붙여라!

 

클래스 형식이 in 파라미터로 전달될 때 복사 생성자 호출가 호출되는 오버헤드를 줄이기 위해 보통 레퍼런스(&)를 붙입니다.

그래서 이때의 클래스 형식에 모두 const가 붙게 됩니다.

함수와 const의 세 가지 경우 중 매개 변수 자료형에 붙는 const가 이 in 마라미터(포인터, 레퍼런스)에 붙는 const입니다.

 

두 Point 객체를 비교하기 위한 Compare() 멤버함수가 있다면..

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    bool Compare(const Point& arg)
    {   //arg를 변경시키지 못한다.
        return x==arg.x && y==arg.y ? true : false;
    }
    void Print( )
    {
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);
    Point p2(5,5);

    cout << p1.Compare(p2) << endl;
}
  1. 0

 Compare() 인자로 Point 객체가 전달되므로 불필요한 '오버헤드'를 줄이기 위해 &를 붙였고 in 파라미터이므로 변경을 차단하기 위해 const를 붙입니다.

 

앞으로 버릇처럼 in 파라미터 레퍼런스, 포인터에 const를 붙이세요.

 

2, 함수 선언에 사용되는 const

함수 선언에 사용되는 const는 멤버 함수만 사용 가능합니다.

이유는 멤버 함수에서 객체의 멤버 변수를 변경하지 않기 위한 목적으로 사용되기 때문입니다.

이렇게 함수 선언에 const가 붙은 함수를 const 멤버 함수라고 합니다.

 

함수 선언에 사용되는 const 예제입니다.

 #include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( ) const
    {   // const는 Print() 함수 내에서 x나 y를 변경하지 않는다는 보장!
        cout << x << ", " << y << endl;
        // 만약 x, y를 변경하면 컴파일 에러~
    }
};
void main( )
{
    Point p1(2,3);
    Point p2(5,5);

    p1.Print( );
    p2.Print( );
}
  1. 2, 3
    5, 5

 Print() 함수는 멤버 변수를 변경하기 위한 함수가 아닙니다. 그러므로 기본적으로 Print() 함수는 const 멤버 함수로 만들어져야 합니다.

 

const 객체들은 const 멤버 함수만 호출할 수 있습니다.

멤버 함수는 객체의 상태를 변경할 수 있는 권한을 가지고 있으므로 const 객체들이 호출할 수 있는 함수는 const 멤버 함수뿐입니다.

 

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void SetXY(int _x, int _y)
    {
        x = _x;
        y = _y;
    }
    void Print( ) const
    {   // const는 Print() 함수 내에서 x나 y를 변경하지 않는다는 보장!
        cout << x << ", " << y << endl;
        // 만약 x, y를 변경하면 컴파일 에러~
    }
};
void main( )
{
    const Point p1(2,3);
    Point p2(5,5);

    p1.SetXY(1,1); // <== 에러~
    p1.Print( ); // const 함수이므로 호출가능.
    p1.SetXY(2,2);
    p2.Print( );
}
  1.  에러~

 const 객체는 const 멤버 함수만 호출할 수 있습니다. 비 const 객체는 당연히 const 멤버 함수를 호출 할 수 있습니다.

 

 그래서 멤버 함수의 기능이 객체의 상태를 변경하지 않는 다면 '이유불문' const 멤버 함수로 만들어야 합니다.

 

간단한 예제를 보입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( ) const
    {   
        cout << x << ", " << y << endl;
    }
};
void PrintPoint(const Point& arg)
{
    arg.Print( ); // const 함수가 아니라면 호출되지 않는 웃지 못할 상황이... ㅡㅡ;
}
void main( )
{
    Point p1(2,3);

 

    PrintPoint( p1 );

}

  1. 2, 3

 PrintPoint() 함수는 Point 객체를 복사하지 않기 위해 &를 붙였고 in 파라미터이므로 const를 붙였습니다. 만약 Print()함수가 const함수가 아니라면 arg.Print()는 함수를 호출할 수 없습니다.

 

다시 강조합니다.

멤버 함수의 기능이 객체의 상태를 변경하지 않는 다면 '이유불문' const 멤버 함수로 만들어야 합니다.

 

하나더

const 멤버 함수와 비 const 멤버 함수는 함수 중복이 가능합니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    void Print( )
    {
        cout <<"난 비const : " << x << ", " << y << endl;
    }
    void Print( ) const
    {
        cout <<"난 const : " << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);
    const Point p2(5,5);

    p1.Print( );
    p2.Print( );
}
  1. 난 비const : 2, 3
    난 const : 5, 5

const 멤버 함수와 비 const 멤버 함수가 함수 중복되었다면 비 const 객체는 비 const 멤버 함수를 호출하고 const 객체는 const 멤버 함수를 호출합니다.

 

3, 리턴 자료형에 사용되는 const

 C++에서 함수는 리턴값으로 두 가지 방식을 사용할 수 있습니다.

  • 값 리턴 방식
  • 참조 리턴 방식(포인터나 레퍼런스)

 

값 리턴 방식 예제입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    int GetX( )
    {
        return x;
    }
    void Print( ) const
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);

    cout << p1.GetX( ) << endl;
    p1.Print( );
}
  1. 2
    2, 3

 GetX()함수는 값을 리턴하는 함수입니다. p1.GetX()는 값 자체라고 생각하지면 됩니다.

 

참조 리턴 방식 예제입니다.(레퍼런스)

 #include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    int& GetX( )
    {
        return x;
    }
    void Print( ) const
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);

    p1.GetX( ) = 10;
    p1.Print( );
}
  1. 10, 3

 참조가 리턴되었으므로 값을 변경할 수 있습니다. 이 예제처럼 리턴된 참조를 이용하여 값을 변경할 수 있으며 잠재적인 위험성을 가지게 됩니다.

 

 

 

참조 리턴 방식 예제입니다.(포인터)

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    int* GetX( )
    {
        return &x;
    }
    void Print( ) const
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);

 

    *p1.GetX( ) = 10;

    p1.Print( );
}

  1. 10, 3

 포인터를 사용했을 뿐 예제는 위와 같습니다.

 

 

그래서 참조를 리턴할 때 클라이너트가 값을 변경하지 못하도록 하려면 아래와 같이 const를 붙여야 합니다.

 

복사하지 않기 위해 레퍼런스(&)를 리턴했다면 const를 붙입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    const int& GetX( )
    {
        return x;
    }
    void Print( ) const
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);

    cout << p1.GetX( ) << endl; // 가능 읽기 참조 
    p1.GetX( ) = 10 ; // <= 에러~~ 변경 불가
    p1.Print( );

}

 

단지 주소가 필요하여 리턴했다면 const를 붙입니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public :
    Point(int _x=0, int _y=0):x(_x),y(_y)
    {
    }
    const int* GetX( )
    {
        return &x;
    }
    void Print( ) const
    {  
        cout << x << ", " << y << endl;
    }
};
void main( )
{
    Point p1(2,3);

    cout << *p1.GetX( ) << endl; // 가능 읽기 참조
    *p1.GetX( ) = 10 ; // <= 에러~~ 변경 불가
    p1.Print( );
}


'Programming > C,C++' 카테고리의 다른 글

strstr 구현하기  (0) 2017.04.17
간단한 thread 예제  (0) 2017.04.16
C, const vs 가변인자 [펌]  (0) 2017.04.07
Stack (Double Linked), C++  (0) 2017.04.07
Queue (Double Linked), C++  (0) 2017.04.06
Posted by 루나s
,