생성자
class SpreadsheetCell
{
public:
SpreadsheetCell(double initialValue);
SpreadsheetCell(string initialValue);
}
SpreadsheetCell::SpreadsheetCell(double initialValue){
setValue(initialValue);
}
SpreadsheetCell::SpreadsheetCell(string initialValue){
setString(initialValue);
}
스택생성자
SpreadsheetCell myCell(5), anotherCell(4);
cout<<"cell 1: "<<myCell.getValue() << endl;
cout<<"cell 2: "<<anotherCell.getValue() << endl;
힙 객체와 생성자
SpreadsheetCell *myCellp = new SpreadsheetCell(5);
SpreadsheetCell *anotherCellp = nullptr;
anotherCellp = new SpreadsheetCell(4);
// --- 셀 객체를 이용 ---
delete myCellp;
myCellp = nullptr;
delete anotherCellp;
anotherCellp = nullptr;
복수생성자 → overloading
SpreadsheetCell aThirdCell("test");
SpreadsheetCell aFourthCell(4.4);
SpreadsheetCell* aThirdCellp = new SpreadsheetCell("4.4");
명시적인 디폴트 생성자
class MyClass{
public :
MyClass() = **default;**
MyClass(int i);
}
명시적으로 삭제된 생성자
class MyClass{
public:
MyClass() = delete;
}
생성자 초기화 리스트(constructor initializer) : 생성자 바디에서 코드를 통해 초기화 하는 것보다 훨씬 효율적임.
SpreadsheetCell::SpreadsheetCell():mValue(0), mString(""){
}
constructor initializer를 통해서만 초기화할 수 있는 경우
class에서 선언된 멤버변수의 순서에 따라 리스트가 초기화 된다.
복제생성자 : 객체의 복제본을 만들어야할 때 사용
프로그래머가 명시적으로 복제 생성자를 만들지 않으면 컴파일러가 자동으로 만들어준다.
컴파일러가 만들어주는 복제 생성자는 기본타입 멤버는 그대로 복제하고 객체타입 멤버는 그 객체의 복제 생성자를 호출해준다.
class SpreadsheetCell{
public:
SpreadsheetCell(const SpreadsheetCell& src);
}
복제 생성자에서는 파라미터로 받는 원본 객체에 대해 상수 참조를 하므로 const와 &가 붙어있다.
다른 객체와 똑같은 객체를 생성할때 복제생성자를 사용한다.
생성자 안에서 원본 객체에 있는 데이터 멤버를 모두 복사한다.
SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src) : mValue(src.mValue){
}
복제생성자가 호출되는 경우
C++에서 하무에 인수를 전달할 때 기본적으로 값으로 전달된다(값 전달방식이 적용된다.)
다시말해 함수나 메서드는 값이나 객체의 복사본을 받는다. 따라서 함수나 메서드에 객체를 전달하면 컴파일러는 그 객체의 복제 생성자를 호출하는 방식으로 초기화 한다.
예를들어, 다음과 같이 string 매개변수를 값으로 받는 printString() 함수가 있다고 하자.
void printString(string inString){
cout<<inString<<endl;
}
string은 기본타입이 아니라 클래스이다. 그래서 코드에서 printString()에 string 매개변수를 전달해서 호출하면 string 매개변수인 inString은 이 클래스의 복제생성자를 호출하는 방식으로 초기화 된다. 이 복제 생성자의 인수가 바로 printString()에 전달한 string이다.
명시적인 복제 생성자 호출
SpreadsheetCell myCell2(4);
SpreadsheetCell myCell3(myCell2); //myCell3는 myCell2 와 같은 값을 가진다.
initializer_list 생성자
class PointSequence
{
public:
PointSequence(initializer_list<double> args){
if(args.size()%2 != 0){
throw invalid_argument("initializer_list should contain even number of elements");
}
for(auto iter = args.begin(); iter != args.end(); ++iter){
mVecPoints.push_back(*iter);
}
//for(const auto& value : args){
// mVecPoints.push_back(value);
//}
}
void dumpPoints() const{
for(auto citer = mVecPoints.cbegin(); citer != mVecPoints.cend(); citer +=2){
cout<<"(" << *citer << ", " << *(citer+1) << ")" << endl;
}
}
protected:
vector<double> mVecPoints;
}
생성자 위임
생성자 안에서 같은 클래스의 다른 생성자를 호출하는 것
생성자 바디에서 다른 생성자를 부를 수는 없고 다음처럼 생성자 초기화 리스트를 이용해야한다.
SpreadsheetCell::SpreadsheetCell(const string& initialValue)
: SpreadsheetCell(stringToDouble(initialValue)){}
생성자 위임할 때는 재귀적으로 꼬리를 무는 상황을 피해야 한다.
객체가 소멸할 때는 두가지 작업이 발생
- 객체의 소멸자가 호출되고 난 후, 할당받은 메모리가 반환
소멸자를 정의하지 않으면, 컴파일러가 자동으로 생성해줌
자동으로 생성된 소멸자는 클래스 내 멤버들을 돌아가며 재귀적으로 소멸자를 호출하여 객체가 삭제될 수 있도록 한다.
스택에 생성된 객체는 유효범위에서 벗어날 때 자동으로 삭제된다.
스택에 생성된 객체는 생성 순서의 역순으로 삭제된다.
SpreadsheetCell myCell2(4);
SpreadsheetCell anotherCell2(5); // myCell2은 anotherCell2보다 먼저 생성된다.
//myCell2은 anotherCell2보다 뒤에 소멸한다.
이러한 생성/소멸 순서는 객체의 데이터 멤버에도 동일하게 적용된다.
생성자에서 멤버 초기화를 할때 복제 생성자가 사용되는지 대입 연산자가 사용되는지 잘 생각해야한다.
데이터 멤버 중 객체가 있으면, 컴파일러가 자동으로 생성하는 복제 생성자에서는 멤버들의 복제 생성자를 재귀적으로 호출한다.
생성자
class SpreadsheetCell
{
public:
SpreadsheetCell(double initialValue);
SpreadsheetCell(string initialValue);
}
SpreadsheetCell::SpreadsheetCell(double initialValue){
setValue(initialValue);
}
SpreadsheetCell::SpreadsheetCell(string initialValue){
setString(initialValue);
}
스택생성자
SpreadsheetCell myCell(5), anotherCell(4);
cout<<"cell 1: "<<myCell.getValue() << endl;
cout<<"cell 2: "<<anotherCell.getValue() << endl;
힙 객체와 생성자
SpreadsheetCell *myCellp = new SpreadsheetCell(5);
SpreadsheetCell *anotherCellp = nullptr;
anotherCellp = new SpreadsheetCell(4);
// --- 셀 객체를 이용 ---
delete myCellp;
myCellp = nullptr;
delete anotherCellp;
anotherCellp = nullptr;
복수생성자 → overloading
SpreadsheetCell aThirdCell("test");
SpreadsheetCell aFourthCell(4.4);
SpreadsheetCell* aThirdCellp = new SpreadsheetCell("4.4");
명시적인 디폴트 생성자
class MyClass{
public :
MyClass() = **default;**
MyClass(int i);
}
명시적으로 삭제된 생성자
class MyClass{
public:
MyClass() = delete;
}
생성자 초기화 리스트(constructor initializer) : 생성자 바디에서 코드를 통해 초기화 하는 것보다 훨씬 효율적임.
SpreadsheetCell::SpreadsheetCell():mValue(0), mString(""){
}
constructor initializer를 통해서만 초기화할 수 있는 경우
class에서 선언된 멤버변수의 순서에 따라 리스트가 초기화 된다.
복제생성자 : 객체의 복제본을 만들어야할 때 사용
프로그래머가 명시적으로 복제 생성자를 만들지 않으면 컴파일러가 자동으로 만들어준다.
컴파일러가 만들어주는 복제 생성자는 기본타입 멤버는 그대로 복제하고 객체타입 멤버는 그 객체의 복제 생성자를 호출해준다.
class SpreadsheetCell{
public:
SpreadsheetCell(const SpreadsheetCell& src);
}
복제 생성자에서는 파라미터로 받는 원본 객체에 대해 상수 참조를 하므로 const와 &가 붙어있다.
다른 객체와 똑같은 객체를 생성할때 복제생성자를 사용한다.
생성자 안에서 원본 객체에 있는 데이터 멤버를 모두 복사한다.
SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src) : mValue(src.mValue){
}
복제생성자가 호출되는 경우
C++에서 하무에 인수를 전달할 때 기본적으로 값으로 전달된다(값 전달방식이 적용된다.)
다시말해 함수나 메서드는 값이나 객체의 복사본을 받는다. 따라서 함수나 메서드에 객체를 전달하면 컴파일러는 그 객체의 복제 생성자를 호출하는 방식으로 초기화 한다.
예를들어, 다음과 같이 string 매개변수를 값으로 받는 printString() 함수가 있다고 하자.
void printString(string inString){
cout<<inString<<endl;
}
string은 기본타입이 아니라 클래스이다. 그래서 코드에서 printString()에 string 매개변수를 전달해서 호출하면 string 매개변수인 inString은 이 클래스의 복제생성자를 호출하는 방식으로 초기화 된다. 이 복제 생성자의 인수가 바로 printString()에 전달한 string이다.
명시적인 복제 생성자 호출
SpreadsheetCell myCell2(4);
SpreadsheetCell myCell3(myCell2); //myCell3는 myCell2 와 같은 값을 가진다.
initializer_list 생성자
class PointSequence
{
public:
PointSequence(initializer_list<double> args){
if(args.size()%2 != 0){
throw invalid_argument("initializer_list should contain even number of elements");
}
for(auto iter = args.begin(); iter != args.end(); ++iter){
mVecPoints.push_back(*iter);
}
//for(const auto& value : args){
// mVecPoints.push_back(value);
//}
}
void dumpPoints() const{
for(auto citer = mVecPoints.cbegin(); citer != mVecPoints.cend(); citer +=2){
cout<<"(" << *citer << ", " << *(citer+1) << ")" << endl;
}
}
protected:
vector<double> mVecPoints;
}
생성자 위임
생성자 안에서 같은 클래스의 다른 생성자를 호출하는 것
생성자 바디에서 다른 생성자를 부를 수는 없고 다음처럼 생성자 초기화 리스트를 이용해야한다.
SpreadsheetCell::SpreadsheetCell(const string& initialValue)
: SpreadsheetCell(stringToDouble(initialValue)){}
생성자 위임할 때는 재귀적으로 꼬리를 무는 상황을 피해야 한다.
객체가 소멸할 때는 두가지 작업이 발생
- 객체의 소멸자가 호출되고 난 후, 할당받은 메모리가 반환
소멸자를 정의하지 않으면, 컴파일러가 자동으로 생성해줌
자동으로 생성된 소멸자는 클래스 내 멤버들을 돌아가며 재귀적으로 소멸자를 호출하여 객체가 삭제될 수 있도록 한다.
스택에 생성된 객체는 유효범위에서 벗어날 때 자동으로 삭제된다.
스택에 생성된 객체는 생성 순서의 역순으로 삭제된다.
SpreadsheetCell myCell2(4);
SpreadsheetCell anotherCell2(5); // myCell2은 anotherCell2보다 먼저 생성된다.
//myCell2은 anotherCell2보다 뒤에 소멸한다.
이러한 생성/소멸 순서는 객체의 데이터 멤버에도 동일하게 적용된다.
생성자에서 멤버 초기화를 할때 복제 생성자가 사용되는지 대입 연산자가 사용되는지 잘 생각해야한다.
데이터 멤버 중 객체가 있으면, 컴파일러가 자동으로 생성하는 복제 생성자에서는 멤버들의 복제 생성자를 재귀적으로 호출한다.
출처 : 전문가를 위한 C++
'C++ STL, 알고리즘' 카테고리의 다른 글
C++ 상속 (3) | 2024.10.02 |
---|---|
const (0) | 2024.10.02 |
OOP (0) | 2024.10.02 |
C++ 알쓸신잡 (0) | 2024.10.02 |
c++ static (0) | 2024.10.02 |