[C++] 생성자(Constructor)에서 조금 알아두면 좋은 읽을거리
안녕하세요.
C++언어를 알고 있는 분이라면, 생성자와 소멸자에 대해서 기본적인 것은 다 잘 알고 있을 거라고 생각합니다. 그중에서 한 번쯤 다시 기억하고 있으면 좋은 것에 대해서 공유해 보겠습니다.
1. 생성자(Constructor)에 관해
생성자(Constructor)라고 하면 기본적으로 객체가 생성될 때 자동으로 호출되는 특수한 멤버 함수입니다. 아래와 같은 기본적인 특징을 가지고 있습니다.
클래스와 같은 이름을 가지며 반환형이 없다.
객체 초기화를 담당한다.
매개변수를 받을 수 있으며, 여러 개의 생성자를 오버로딩할 수 있다.
1) 생성자 인자
생성자 인자가 다르면 디폴트 생성자의 형태도 같아야 합니다. 함수 오버로딩과 비슷할 수 있는데 객체 생성할 때 생성자도 이것을 지켜야 합니다. (디폴트 값을 넣어서 유사하게 처리할 수도 있습니다.)
아래 소스코드에서 처럼 Car car1 선언하면 생성자가 정상적으로 실행합니다.
#include <iostream>
using namespace std;
class Car {
string brand;
public:
Car(string b) { // 매개변수가 있는 생성자
brand = b;
cout << brand << " 자동차가 생성되었습니다!" << endl;
}
};
int main(void)
{
Car car1("GoodCar");
//Car car2;
return 0;
}
그러나 Car car2는 인자가 맞지 않아 에러가 발생합니다.
2) 생성자는 public 일경우만 가능하다?
경우에 따라 다를 수 있습니다. 결론을 먼저 말하자면 private에서도 가능합니다.
어떤 경우냐 하면 클래스 외부에서는 생성자 호출을 통해 객체를 생성하는 것이 아닌 클래스 내부에서만 생성하려고 할 경우입니다.
예를 들자면 디자인 패턴에서 나오는 싱글톤 패턴이 있습니다.
아래 예시코드를 참조할 수 있습니다. 조금 어려운 코드일 수 있는데, 메모리 누수를 막기 위한 방법인 스마트 포인터를 추가로 사용했습니다.
#include <iostream>
#include <memory>
using namespace std;
class Singleton {
private:
static unique_ptr<Singleton> instance;
Singleton() { cout << "Singleton 객체 생성!" << endl; }
public:
static Singleton* getInstance() {
if (!instance) {
instance.reset(new Singleton()); // new 호출 (자동 관리)
}
return instance.get();
}
~Singleton() { cout << "Singleton 객체 소멸!" << endl; }
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
// 정적 unique_ptr 초기화
unique_ptr<Singleton> Singleton::instance = nullptr;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
cout << "s1 주소: " << s1 << endl;
cout << "s2 주소: " << s2 << endl;
return 0; // 여기서 instance가 자동으로 소멸됨
}
s1, s2 변수가 달라 다른 객체를 생성할 것 같지만 결과를 보면 동일한 메모리 주소를 가지므로, 같은 Singleton 객체를 참조하고 있다는 것을 보여줍니다. 싱글톤 패턴의 핵심은 바로 유일한 인스턴스를 유지하는 것입니다.
그리고 객체 생성과 소멸에도 문제없이 작동한다는 것을 알 수 있습니다.
클래스 내부에서만 인스턴스 생성해서, 메서드를 통해 단일 인스턴스만 반환합니다. 이런 특정 상태나 리소스를 공유해야 할 때 유용하며 객체가 단 하나만 존재해야 하므로 생성자를 private으로 만들어 외부에서 직접 생성할 수 없게 막을 수 있습니다.
만약 public에 생성자를 사용할 경우, 어디서든 필요할 때 객체를 생성할 수 있습니다. 이는 단일 인스턴스를 유지해야 하는 싱글톤 패턴의 목표에는 맞지 않습니다. 여러 개의 인스턴스가 생성될 수 있으므로, 객체의 일관성을 유지하기 어렵기 때문입니다.
위와 같은 경우, private에 생성자가 사용되기도 합니다.
참고로, 위의 코드는 싱글톤 패턴으로 사용할 수 있는 코드이고 메모리 누수도 막을 수 있지만, 스레드에서 사용 시 추가적인 보완(std::call_once 등)을 해주어야 합니다. 코드는 사용하는 곳과 시기에 따라 항상 보완이 필요합니다. ~~
감사합니다.