이번 포스팅에서는 POCU 아카데미 강의를 들으며 C++의 인라인 함수, 그리고 static 키워드에 대해 공부한 내용을 정리해 보려고 한다.
인라인 함수
함수를 호출할 때는 다음과 같은 단계를 거친다. 함수는 메모리 안에 "할당"되어 있다.
- 변수들을 스택에 push
- 함수 주소로 점프
- 함수를 실행
- 호출자 함수로 다시 점프
- 1번 단계에서 넣어두었던 변수들을 pop
따라서 함수가 저 멀리 저장공간(메모리)에 있는 경우는 CPU 캐시에 저장이 되어있지 않기 때문에 최적화가 이루어지지 않게 된다. 따라서 모든 걸 함수로 만들라는 조언은 적합하지 않다. 물론 함수를 써서 가독성이 좋아질 수도 있기에, 좋다고 볼 수도 있지만 함수를 호출함으로써 필요한 오버헤드를 떠맡는 것이 부담이 됨은 기억해야 한다. C++에서는 인라인 함수를 통해서 이러한 문제를 해결한다.
인라인 함수는 다음과 같이 inline 키워드를 함수 앞에 붙여서 사용한다.
// 인라인 멤버함수
// Animal.h
class Animal
{
public:
Animal(int age);
inline int GetAge() const;
}
int Animal::GetAge() const
{
return mAge;
}
// 멤버 아닌 인라인 함수
// Math.h
inline int Square(int number)
{
return number * number;
}
인라인 함수는 복붙과 사실 비슷하다. 컴파일 하는 도중에 함수를 가져다 준다는 점에서 매크로와 조금 다르다. 매크로를 그냥 써도 되지 않을까? 라고 생각할 수 있겠지만, 매크로는 디버깅하기가 어렵고 범위(scope)를 준수하지 않아서 가능하면 인라인 함수를 쓰는 것을 권장한다.
인라인 함수의 동작원리는 다음과 같다.
인라인 함수를 쓸 때 주의해야 할 점이 있다. 우선 인라인 키워드를 쓴다고 100% 인라인이 되는 것은 아니다. 우리는 인라인을 신청(?) 할 뿐이고 결정은 컴파일러가 한다 ㅋㅋ 그리고 인라인 함수 구현이 헤더 파일에 위치해야 한다. 컴파일러는 cpp 파일을 다 따로 컴파일하는데 다른 cpp 파일의 인라인 함수를 가져올 때 구현체가 없으면 안되기 때문에 항상 인라인 함수는 헤더 파일 안에 구현이 되어 있어야 한다.
static 키워드
static은 C++에서 굉장히 남용하기 쉬운 키워드이다. C나 JAVA에서도 다르게 많이 쓰이기 때문에 C++에서 static에 대해서 이번에 확실하게 정리를 하고 넘어가고자 한다.
static은 기본적으로 범위(scope)의 제한을 받는 전역변수이다. 그 범위는 파일, 네임스페이스, 클래스, 함수 등이 될 수가 있다.
C++에는 extern 키워드가 있어서 다른 파일의 전역변수에 접근이 가능했었다. 하지만 이를 원치 않는 경우 변수 앞에 static 키워드를 붙여주어 다른 파일에서 해당 변수를 접근하지 못하게 할 수가 있다.
// StaticTest.h
extern int globalValue;
void IncreaseValue();
// StaticTest.cpp
#include "StaticTest.h"
static int globalValue = 2;
void IncreaseValue()
{
globalValue++;
}
// Main.cpp
#include "StaticTest.h"
int main()
{
// 링커(linker) 에러
printf("%d", globalValue);
return 0;
}
또한 static은 함수 안에서 범위를 제한하여 정적 변수로도 사용할 수 있다. 또한 클래스 내에서 정적 멤버변수로도 사용을 할 수 있다. 베스트 프랙티스는 함수 안에서 정적 변수를 넣지 않고 클래스 안에 넣는 것이며, 전역변수는 가능한 쓰지 않는 것이다.
다음의 코드를 한 번 보고 메모리에 mCount 변수가 몇 개 존재하는지, 그리고 mCount의 값은 무엇인지 한 번 예상해 보자.
// Cat.h
class Cat
{
public:
Cat(int age, const string& name);
private:
static int mCount;
};
// Cat.cpp
int Cat::mCount = 0;
Cat::Cat(int age, const string& name)
{
++mCount;
}
// Main.cpp
Cat* myCat = new Cat(2, "Coco");
Cat* yourCat = new Cat(5, "Momo");
Cat* hisCat = new Cat(3, "Nana");
Cat* herCat = new Cat(7, "Coco");
결과는 mCount는 메모리에 하나만 존재하며 mCount의 값은 4이다.
정적 멤버 함수는 논리적인 범위에 제한된 전역 함수이다. 해당 클래스의 정적 멤버에만 접근이 가능하다. 개체가 없이도 정적 함수를 호출할 수 있다. 만약 정적 메서드에서 비정적 멤버 변수를 쓰려고 하면 컴파일이 되지 않는다. 비정적 멤버 변수는 오브젝트에서만 참조가 가능한데 GetName() 함수가 오브젝트에 있는 함수가 아니라 클래스에 있는 함수이기 때문이다.
// Cat.h
class Cat
{
public:
Cat(const std::string& name);
static const char* GetName();
private:
char* mName;
static int mCount;
};
// Cat.cpp
int cat::mCount = 0;
Cat::Cat(const std::string& name)
{
++mCount;
}
const char* Cat::GetName()
{
return mName; // Compile Error (a nonstatic member reference must be relative to a specific object)
}
오늘은 인라인 함수와 정적 키워드에 대해서 알아보았다. 둘 다 중요한 개념이므로 잘 기억하고 C++ 프로그래밍을 하면 좋을 것 같다.
참고자료
- C++ 언매니지드 프로그래밍, 포큐 아카데미
'Prog. Langs & Tools > C++' 카테고리의 다른 글
[C++] Ch10. STL - 벡터(Vector) (0) | 2021.03.31 |
---|---|
[C++] Ch09. 예외(Exception) (0) | 2021.01.11 |
[C++] Ch07. 캐스팅(Casting) (0) | 2020.11.16 |
[C++] Ch06. OOP 3 (0) | 2020.10.19 |
[C++] Ch05. OOP 2 (0) | 2020.10.01 |