💬 윤성우 님의 <열혈 C++ 프로그래밍> 책을 혼자 공부하며 배운 내용을 정리합니다. 글의 모든 내용은 책에서 발췌하였습니다.
const
const
로 어떤 객체를 선언하면, 이 객체를 대상으로는 const
멤버함수만 호출이 가능하다. const
객체란 데이터의 변경을 허락하지 않는데, 변경할 수 있는 가능성이 있는 함수는 호출 자체를 제한한다.
따라서 멤버 변수를 호출하지 않는 함수는 가급적 const
를 붙여서 const
객체에서도 호출할 수 있도록 할 필요가 있다. const
키워드는 많을수록 좋다.
const
오버로딩앞서 오버로딩은 함수의 인자 수 또는 자료형이 달라야 한다고 했는데, const
의 유무 또한 오버로딩의 조건에 해당된다.
따라서 다음과 같은 오버로딩은 가능하다.
void someFunc() {}
void someFunc () const {}
어떤 객체 안에 같은 함수가 상수형, 비상수형으로 정의되어 있다면, 함수를 호출하는 객체가 상수냐 비상수냐에 따라 호출되는 함수가 달라진다.
friend
friend는 친구라는 뜻이다. 친구에게는 내 사적인 비밀까지 말할 수 있다.
어떤 클래스 A가 다른 클래스 B를 친구로 선언하면, B에서는 A의 private 멤버에 접근할 수 있다.
class Boy
{
private:
int height;
friend class Girl;
public:
Boy(int n) : height(n)
{}
};
class Girl
{
public:
void Func(Boy &frn)
{
cout << "Boy's height : " << frn.height << "\n";
}
};
int main()
{
Boy b(100);
Girl g;
g.Func(b);
}
Boy
클래스에서 Girl
클래스를 friend 선언했으므로, Girl
클래스에서는 Boy
클래스의 멤버 변수 height
이 private
으로 선언되었음에도 불구하고 접근하여 출력할 수 있다.
friend
선언전역 함수 혹은 클래스의 멤버 함수 또한 friend
키워드를 사용할 수 있다.
A 클래스에서 B 클래스의 멤버 함수 또는 전역 함수를 friend
선언하면, B 클래스 객체 혹은 전역에서 A 클래스의 private 멤버에 접근할 수 있다.
friend
, 언제 사용하나?객체지향의 의의는 정보의 은닉에 있다. 그러나 friend 문법은 이 정보은닉을 무너뜨리는 문법이다.
나같은 초보들의 경우 private
변수를 이곳저곳에서 사용하기 위해 friend를 남발하는 경우가 있다는데 이는 문제를 더 크게 만든다.
가급적 사용하지 않는 것이 좋다고 한다.
static
C++에서도 C의 static
키워드의 의미가 통용된다.
static
의 의미를 잠깐 다시 상기하고 간다면 두 가지 개념으로 정리할 수 있다.
각 객체가 몇 번 생성되었는지 카운트하기 위하여, 정적변수 말고 전역변수를 사용할 수 있다.
그러나 전역변수로 사용하게 되면 다른 모든 곳에서도 접근이 가능하기 때문에 좋지 않다. 따라서 클래스 내의 정적 변수로 선언하는 것이 좋다.
class C
{
public:
static int count;
C()
{
count += 1;
}
void printCount()
{
cout << "count : " << count << "\n";
}
};
int C::count = 0;
int main()
{
C c1;
C c2;
C c3;
c1.printCount();
c2.printCount();
c3.printCount();
}
실행 결과 각각의 객체가 같은 위치의 정적 변수 count를 공유하기 때문에 생성 시마다 1씩 증가하여 3이 3번 출력된다.
이 정적 변수는 객체 내에 존재하지 않고, 다만 클래스 객체 내에서 접근할 수 있는 권한이 있다.
정적 변수는 컴파일 시에 이미 메모리 공간에 할당이 이뤄지기 때문에 객체의 생성자로 초기화하면 안 된다. 따라서 클래스 외부 전역에서 초기화를 진행해 주어야 한다.
그리고 정적 멤버변수가 위처럼 public
으로 선언이 되면 어디서든 접근이 가능하다. 객체 안에 존재하지 않기 때문이다.
int main()
{
C c1;
C c2;
C c3;
cout << C::count << "\n";
}
static
멤버 함수정적 멤버 함수 역시 정적 멤버 변수와 동일한 특성을 가진다.
public
으로 선언되면 클래스 이름을 이용해 호출이 가능함다음과 같은 코드는 컴파일 에러를 발생시킨다.
class C
{
private:
int num1;
static int num2;
public:
static void addNum(int n)
{
num1 += n; // 에러 발생!
num2 += n;
}
}
언뜻 보면 멤버함수가 멤버변수 안에 접근하니 문제가 없을 것 같지만, 정적 함수는 객체의 멤버로 존재하지 않는다
는 성질을 상기하면 이해가 가능하다.
private
변수에 접근이 불가능함정적 멤버함수 내에서는 정적으로 선언된 멤버함수/변수만 호출 가능하다.
따라서 위의 num2 += n;
은 문제없는 코드이다.
const static
멤버챕터 4 에서, const
로 선언된 멤버 변수는 : num1(n)
와 같이 멤버 이니셜라이저를 이용해 초기화해야 한다고 하였다. 그러나 정적 상수 멤버 변수는 선언과 동시에 초기화를 할 수 있다.
그리고 정적 변수이기 때문에 클래스 객체가 생성되지 않았더라도 접근이 가능하다.
class C
{
public:
const static int A = 100;
};
int main()
{
cout << C::A << "\n";
// 출력 결과 : 100
}
mutable
'변하기 쉬운' 이라는 뜻의
mutable
키워드는const
함수 내에서의 값의 변경을 예외적으로 허용한다.
mutable
은 가급적 사용을 지양해야 하는 키워드이긴 하다.
const
함수란, 그 함수 내에서 멤버 변수의 값을 변경하지 않겠다는 뜻이었다.
다음 코드를 살펴보자
class C
{
private:
int num1;
mutable int num2;
public:
C(int n1, int n2) : num1(n1), num2(n2)
{}
void showData() const
{
cout << num1 << num2 << "\n";
}
void copyData() const
{
num2 = num1;
}
};
int main()
{
C a(10, 20);
a.showData();
a.copyData();
a.showData();
/*
실행 결과
10 20
10 10
*/
}
copyData
함수는 상수 멤버함수이다. 그러므로 멤버 변수의 값이 변경되면 안된다. 그러나 num2 = num1;
으로 num2
의 값을 변경시키고 있다.
그러나 num2
가 상수 함수 내에서도 값이 변할 수 있는 mutable
키워드로 선언되었기 때문에 문제를 발생시키지 않고, 실제로 값도 변한다.