본문 바로가기

Prog. Langs & Tools/C++

[C++] Ch08. 인라인 함수, static 키워드

 

이번 포스팅에서는 POCU 아카데미 강의를 들으며 C++의 인라인 함수, 그리고 static 키워드에 대해 공부한 내용을 정리해 보려고 한다.

 

인라인 함수

함수를 호출할 때는 다음과 같은 단계를 거친다. 함수는 메모리 안에 "할당"되어 있다.

  1. 변수들을 스택에 push
  2. 함수 주소로 점프
  3. 함수를 실행
  4. 호출자 함수로 다시 점프
  5. 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++] Ch08. 인라인 함수, static 키워드  (0) 2020.11.30
[C++] Ch07. 캐스팅(Casting)  (0) 2020.11.16
[C++] Ch06. OOP 3  (0) 2020.10.19
[C++] Ch05. OOP 2  (0) 2020.10.01