본문 바로가기

Cpp

객체 사용 전 객체 초기화

C++ 에서 객체 초기화는 상황에 따라 다른 모습을 보인다.

int x;

는 확실히 0으로 초기화된다.

 

그런데

class Point{

int x,y;
};

여기서는 초기화가 보장 될 수 도 있고 아닐 수 있다.....

 

일단 C 부분만을 기준으로 "초기화에 런타임 비용이 소모 될 가능성이 있다면 초기화가 보장되지 않는다."

 

여기서 중요한 것은 C 가 아닌 부분이 겹치면 상황이 애매해진다는 것이다.

 

이것은 C 배열은 원소가 확실히 초기화된다는 보장이 없지만 vector(STL) 는 보장해준다   <<< 이 상황을 의미한다.

따라서 생성 시 혹은 객체의 사용 전 가능한 항상 초기화하는 것이다.

 

참고로 생성자에서 초기화 리스트를 사용하지 않는다면 그것은 대입 방식이다,

 

class PhoneNumber { 
    // PhoneNumber 클래스 정의
};

class ABEntry { // ABEntry = "Address Book Entry"
public:
    ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones);

private:
    std::string theName;
    std::string theAddress;
    std::list<PhoneNumber> thePhones;
    int numTimesConsulted;
};

ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
{
    theName = name;             // 지금은 모두 '대입'을 하고 있습니다.
    theAddress = address;       // '초기화'가 아닙니다.
    thePhones = phones;
    numTimesConsulted = 0;
}

 

이렇게 대입 방식을 이용하면 해당 객체는 초기화를 2번 한다. 이것은 각각의 멤버에 대한 기본 생성자를 호출해서 초기화를 미리 하고 복자 생성자로 객체 생성자에서 대입하는 것입니다. 그러니 객체 초기화 과정은 지나간 이후 이다,

 

즉 초기화 리스트를 사용하여 기본 생성자를 호출하지 않고 복사 생성자만 호출하여 연산을 줄이는 것이 효율적이며 실제 객체의 초기화를 사용하고 생성자에서 대입 연산이 일어나지 않을 것이다.

class PhoneNumber { 
    // PhoneNumber 클래스 정의
};

class ABEntry { // ABEntry = "Address Book Entry"
public:
    ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones);

private:
    std::string theName;
    std::string theAddress;
    std::list<PhoneNumber> thePhones;
    int numTimesConsulted;
};

ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
    : theName(name),              // 초기화 리스트를 사용
      theAddress(address), 
      thePhones(phones), 
      numTimesConsulted(0)        // 멤버 변수를 초기화
{
    // 본문은 비어 있음
}

 

 

기본 제공 자료형 같은 경우는 치기화나 대입에 걸리는 비용 차이가 거의 없지만, 그래도 초기화 리스트에 모두 넣는 것이 좋다.

 

본인의 실수 예방을 위해서라도 그냥 멤버는 초기화 리스트를 이용해주자.

 

그리고 초기화 순서는 선언된 멤버의 순서에 따라서 순차적으로 진행된다. 이것은 초기화 리스트의 순서와는 초기화 순서가 관련 없다는 말이다.

 

정적 객체 (static Object)

정적 객체는 생성된 시점부터 프로그램이 끝날 때까지 살아 있다.

 

그러니 스택이나 힙에 할당된 객체들은 애초에 정적 객체화 되어 질 수 없다,

 

정적 개체는

1. 전역 객체

2. 네임스페이스 유효범위에서 정의된 객체

3. 클래스 안에서 static 으로 선언된 객체

4. 함수 안에서 static 으로 선언된 객체

5. 파일 유효범위에서 static 으로 정의된 객체가 있다.

 

함수 안 정적 객체를 지역 정적 객체라 한다. 나머지는 비지역 정적 객체이다.

 

번역 단위는 컴파일을 통해 하나의 목적 파일을 만드는 바탕이 되는 소스 코드이다.

기본적으로 소스 파일 하나가를 의미하는데 해당 파일에 #include 하여 하나의 번역 단위가 되는 것이ㅏㄷ.

 

여기서 ' 별개의 번역 단위에서 정의된 비지역 정적 객체들의 초기화 순서는 정해져 있지 않다. '  이것은

번역 단위가 2개인 지역에서 각각의 정적 객체들을 사용하려하면 초기화 되지 않아 오류가 날 수 있다느 것이다,

 

이때 지역 정적 객체를 만드는 함수를 만들어 사용하면 문제 해결된다. 이 말은 지역 정적 객체는 초기화 시점이 정확히 알 수 있다느 것을 이용하는 것이다.

 

#include <iostream>
#include <string>

// tfs와 tempDir의 초기화 순서를 제어하는 함수 방식
class FileSystem {
public:
    FileSystem() {
        std::cout << "FileSystem 초기화 완료!" << std::endl;
    }
    void use() const {
        std::cout << "FileSystem 사용 중!" << std::endl;
    }
};

class Directory {
public:
    Directory(const FileSystem& fs) {
        fs.use();
        std::cout << "Directory 초기화 완료!" << std::endl;
    }
};

// FileSystem 객체를 관리하는 함수
FileSystem& getFileSystem() {
    static FileSystem tfs;  // 지역 정적 객체
    return tfs;
}

// Directory 객체를 관리하는 함수
Directory& getTempDirectory() {
    static Directory tempDir(getFileSystem());  // getFileSystem 호출로 tfs 보장
    return tempDir;
}

int main() {
    // 객체 초기화 순서 보장
    getTempDirectory();  // tempDir 초기화
    return 0;
}

 

다음과 같이 사용하면 특정 정적 객체를 먼저 초기화하여 사용 가능하다.

 

또한 정적 객체에 대해 지연 초기화를 통해 따로 사용 안한다면 객체 생성 비용도 들지 않는다,

 

참조 반환형 함수를 통해 비지역 정적 객체를 지역 정적 객체화하여 사용한다.

 

멀티스레드에서 비지역 정적 객체의 위험성

다중 스레드에서 비지역 정적 객체를 사용하는 것은 시한폭탄을 들고 있는 것이다. 무조건 참조자 반환 함수를 호출해서 초기화와 관련된 Race Condition 은 사라질 수 있다. ( 그래도 C++11 이상 기준에서 정적 객체의 초기화는 스레드 안정성이 보장된다)   --- 초기화 스레드 안정성 보장임을 알아야한다.

 

다만, 위와 같이 a 객체 초기화가 b 객체에 의존적이라면 개망하니까 조심하자

 

 

'Cpp' 카테고리의 다른 글

const 사용  (1) 2024.12.15
#define 대신 const, enum, inline 을 생각 할 것  (2) 2024.12.14
C++의 하위 언어  (0) 2024.12.14
바이트 패딩  (0) 2024.11.18
RAII란?  (0) 2024.11.02