객체 생성
스택(에 생성되는) 객체는 선언하는 시점에 생성되고,
스마트 포인터나 new, new[]를 사용할 때는 직접 공간을 할당해야 한다.
예를 들면 다음과 같다.
#include <string>
class MyClass
{
private:
std::string mName;
};
int main()
{
MyClass obj;
return 0;
}
MyClass 안에 있는 string 객체(mName)는 main() 함수에서 MyClass 객체가 생성될 때 함께 생성되고,
MyClass 객체가 소멸될 때 함께 소멸된다.
그래서 변수를 선언할 때 초깃값을 설정하는 것이 좋다.
예를 들면 다음과 같다.
int x = 0;
이 작업은 생성자라 부르는 특수한 메서드에 객체를 초기화하는 코드를 작성하는 방식으로 처리할 수 있다.
1. 생성자 작성 방법
생성자 이름은 클래스 이름과 똑같이 지정한다.
생성자는 리턴 값이 없으며 필요에 따라 매개변수를 받을 수 있다.
아무런 인수를 주지 않고 호출하는 생성자를 디폴트 생성자라 부른다.
이전에 만들었던 SpreadsheetCell 클래스에 생성자를 추가한 버전을 만들어보자.
class SpreadsheetCell
{
public:
SpreadsheetCell(double initialValue);
// 클래스 정의의 나머지 부분은 생략함
};
일반 메서드마다 구현 코드를 반드시 작성하듯이 생성자도 구현 코드를 작성해야 한다.
SpreadsheetCell::SpreadsheetCell(double initialValue)
{
setValue(initialValue);
}
SpreadsheetCell 생성자도 일종의 SpreadsheetCell 클래스 멤버다.
따라서 C++에서는 생성자 이름 앞에 SpreadsheetCell::이라는 스코프 지정 연산자를 붙여야 한다.
생성자 이름은 클래스 이름인 SpreadsheetCell이다.
2. 생성자 사용법
객체는 생성자를 통해 생성되고 그 객체의 값을 초기화할 수 있다.
스택 객체와 힙 객체 모두 생성자를 사용할 수 있다.
#스택 객체 생성자.
스택에 할당한 SpreadsheetCell 객체의 생성자를 호출하는 방법은 다음과 같다.
SpreadsheetCell myCell(5), anotherCell(4);
cout << "cell 1: " << myCell.getValue() << endl;
cout << "cell 2: " << anotherCell.getValue() << endl;
이때 SpreadsheetCell 생성자를 다음과 같이 선언과 동시에 호출하면 안 된다.
SpreadsheetCell myCell.SpreadsheetCell(5); // 컴파일 에러가 발생한다.
마찬가지로 다음과 같이 선언한 뒤에 호출해도 안 된다.
SpreadsheetCell myCell;
myCell.SpreadsheetCell(5); // 컴파일 에러가 발생한다.
#힙 객체 생성자.
SpreadsheetCell 객체를 동적으로 할당할 때 생성자를 호출하는 방법은 다음과 같다.
auto smartCellp = make_unique<SpreadsheetCell>(4);
// ... 셀을 다루는 코드를 작성한다. 스마트포인터라면 직접 삭제하지 않아도 된다.
// 일반 포인터를 사용해도 되지만 권장하지 않는다.
SpreadsheetCell* myCellp = new SpreadsheetCell(5);
SpreadsheetCell* anotherCellp = nullptr;
anotherCellp = new SpreadsheetCell(4);
// ... 셀을 다루는 코드를 작성한다.
delete myCellp; myCellp = nullptr;
delete anotherCellp; anotherCellp = nullptr;
코드를 보면 SpreadsheetCell 객체를 포인터 방식으로 선언할 때 곧바로 생성자를 호출하지 않아도 된다.
이와 달리 스택 객체는 선언할 때 생성자를 호출한다.
new로 객체를 동적으로 할당했다면 반드시 그 객체를 delete로 해제해야 한다.
스마트 포인터를 사용하면 delete를 사용하지 않아도 자동으로 해제된다.
3. 생성자 여러 개 제공하기
객체는 생성자를 통해 생성되고 그 객체의 값을 초기화할 수 있다.
생성자가 여러 개이더라도 이름은 모두 똑같이 클래스 이름으로 정하고, 인수의 개수나 타입만 서로 다르게 정의한다.
스택 객체와 힙 객체 모두 생성자를 사용할 수 있다.
C++ 코드에 이름이 같은 함수가 여러 개 있으면 컴파일러는 호출하는 시점에 매개변수 타입이 일치하는 함수를 선택한다.
이를 오버로딩이라 부른다.
앞에서 본 SpreadsheetCell 클래스를 생성자를 두 개 갖도록 수정해보자.
하나는 double 타입의 초깃값을 받고, 다른 하나는 string 타입의 초깃값을 받는다. 코드는 다음과 같다.
class SpreadsheetCell
{
public:
SpreadsheetCell(double initialValue);
SpreadsheetCell(std::string_view initialValue);
// 클래스 정의의 나머지 부분은 생략함
};
두 번째 생성자의 구현 코드는 다음과 같다.
SpreadsheetCell::SpreadsheetCell(string_view initialValue)
{
setString(initialValue);
}
이렇게 정의한 생성자를 사용하는 예는 다음과 같다.
SpreadsheetCell aThirdCell("test"); // string 타입의 인수를 받는 생성자 사용
SpreadsheetCell aFourthCell(4.4); // double 타입의 인수를 받는 생성자 사용
auto aFifthCellp = make_unique<SpreadsheetCell>("5.5"); // string 타입의 인수를 받는 생성자 사용
cout << "aThirdCell: " << aThirdCell.getValue() << endl;
cout << "aFourthCell: " << aFourthCell.getValue() << endl;
cout << "aFifthCellp: " << aFifthCellp->getValue() << endl;
생성자가 여러 개면 한 생성자 안에서 다른 생성자를 호출할 수 있다.
예를 들어 다음과 같이 string 타입 인수를 받는 생성자에서 double 타입 인수를 받는 생성자를 호출할 수 있다.
SpreadsheetCell::SpreadsheetCell(string_view initialValue)
{
SpreadsheetCell(stringToDouble(initialValue));
}
이렇게 해도 문제가 없어 보이지만 의도한 대로 실행되지 않는다.
SpreadsheetCell 생성자를 이렇게 직접 호출하면 내부적으로 이름 없는 SpreadsheetCell 타입의 임시 객체가 생성돼서
원래 초기화하려는 객체의 생성자가 호출되지 않기 때문이다.
단, 같은 클래스에서 생성자끼리 호출할 수 있도록 C++에서 제공하는 위임 생성자를 이용하면 가능하다.
'💻프로그래밍 내용 정리 > C++17' 카테고리의 다른 글
[C++ 8.2.3] 객체 사용법 (0) | 2022.10.03 |
---|---|
[C++ 8.2.2-2] this 포인터 (0) | 2022.10.03 |
[C++ 8.2.2-1] 메서드 정의 방법 (0) | 2022.10.03 |
[C++ 8.2.1] 클래스 정의 (0) | 2022.10.03 |
[C++ 7.5.1] 메모리 누수, 비주얼 C++을 이용한 메모리 누수 탐지 및 수정 방법 (0) | 2022.10.03 |