멤버 이니셜라이저(Member Initializer)를 이용한 변수 및 const 상수(변수) 초기화
멤버 이니셜라이저는 객체가 아닌 멤버의 초기화에도 사용할 수 있다.
class simple
{
private:
int num1;
int num2;
public:
simple(int n1, int n2) : num1(n1) // 멤버변수 initializer
{
num2 = n2;
}
....
};
위 클래스에서 보이듯이 객체가 아닌 멤버면수도 이니셜라이저를 통해서 초기화가 가능하다.
num1(n1)
위 문장은 num1을 n1의 값으로 초기화하라는 뜻.
따라서 프로그래머는 [생성자의 몸체에서 초기화하는 방법]과 [이니셜라이저를 이용하는 초기화 방법] 중에서 선택이 가능하다.
그러나 일반적으로 멤버변수의 초기화에 있어서는 이니셜라이저를 선호하는 편.
두 가지 이점이 있기 때문
- 초기화의 대상을 명확히 인식
- 성능에 약간의 이점
C++ 프로그래머들은 이니셜라이저가 더 명확한 표현이라고 함.
위에서 보인 초기화는 아래와 같음
int num1 = n1;
이니셜라이저를 통해서 초기화되는 멤버는 선언과 동시에 초기화가 이뤄지는 것과 같은 유형의 바이너리 코드를 구성하기 때문.
반면, 생성자의 몸체에서 보인 다음의 초기화는
num2=n2;
다음 두 문장에 비유 가능
int num2;
num2=n2;
이니셜라이저를 이용하면 선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성된다.
반면, 생성자의 몸체부분에서 대입연산을 통한 초기화를 진행하면, 선언과 초기화를 각각 별도의 문장에서 진행하는 형태로 바이너리 코드가 생성.
"const 멤버변수도 이니셜라이저를 이용하면 초기화가 가능하다."
예시
#include <iostream>
using namespace std;
class fruitseller
{
private:
const int apple_price;
int numofapples;
int mymoney;
public:
fruitseller(int price, int num, int money) : apple_price(price), numofapples(num), mymoney(money)
{
}
int saleapples(int money)
{
int num = money / apple_price;
numofapples -= num;
mymoney += money;
return num;
}
void showresult() const // "이 함수에서는 맴버 변수의 값을 변경하지 않겠다" and "함수 호출도 const 함수만 호출 가능하도록 한다"
{
cout << "남은 사과 : " << numofapples << endl;
cout << "판매 수익 : " << mymoney << endl << endl;
}
};
class fruitbuyer
{
private:
int mymoney;
int numofapples;
public:
fruitbuyer(int money) : mymoney(money), numofapples(0)
{
}
void buyapples(fruitseller& seller, int money)
{
numofapples += seller.saleapples(money);
mymoney -= money;
}
void showresult() const
{
cout << "현재 잔액 : " << mymoney << endl;
cout << "사과 개수 : " << numofapples << endl << endl;
}
};
int main(void)
{
fruitseller seller(1000, 20, 0);
fruitbuyer buyer(5000);
buyer.buyapples(seller, 2000);
cout << "과일 판매자 현황" << endl;
seller.showresult();
cout << "과일 구매자 현황" << endl;
buyer.showresult();
return 0;
}
이니셜라이저의 이러한 특징은 멤버변수로 참조자를 선언할 수 있게 함
cost 변수와 마찬가지로 '참조자도' 선언과 동시에 초기화가 이뤄져야 한다.
따라서 이니셜라이저를 이용하면 참조자도 멤버변수로 선언될 수 있다.
#include <iostream>
using namespace std;
class AAA
{
public:
AAA()
{
cout << "empty object" << endl;
}
void showname()
{
cout << "I'm class AAA" << endl;
}
};
class BBB
{
private:
AAA& ref; // 참조자가 멤버변수로 선언 이니셜라이저를 통해서 초기화 해야 함
const int& num; // const 참조자 선언, 이니셜라이저를 통해 정수형 상수로도 초기화 가능
public:
BBB(AAA& r, const int& n) :ref(r), num(n)
{
// empty constructor body
}
void showname()
{
ref.showname();
cout << "and" << endl;
cout << "I ref num " << num << endl;
}
};
int main(void)
{
AAA obj1;
BBB obj2(obj1, 29);
obj2.showname();
return 0;
}
디폴트 생성자(default constructor)
메모리 공간의 할당 이후에 생성자의 호출까지 완료되어야 '객체'
즉, 객체가 되기 위해서 반드시 하나의 생성자가 호출
그리고 이러한 기준에 예외를 두지 않기 위해서 생성자를 정의하지 않는 클래스에는 C++ 컴파일러에 의해서 디폴트 생성자라는 것이 자동으로 삽입된다.
디폴트 생성자는 인자를 받지 않으며, 내부적으로 아무런 일도 하지 않는 생성자.
따라서 다음과 같이 정의된 클래스는
class AAA
{
private:
int num;
public:
int getnum{ return num; }
};
아래와 동일하다.
class AAA
{
private:
int num;
public:
AAA(){} // 디폴트 생성자
int getnum{ return num; }
};
Private 생성자
"클래스 내부에서 객체를 생성한다면, 생성자가 private으로 선언 가능"
#include <iostream>
using namespace std;
class AAA
{
private:
int num;
public:
AAA() : num(0) {} // 생성자
AAA& createinitobj(int n) const
{
AAA* ptr = new AAA(n); // 아래 private에서 정의된 생성자를 이용해서 객체 생성
return *ptr;
}
void shownum() const
{
cout << num << endl;
}
private:
AAA(int n) : num(n) {}
};
int main(void)
{
AAA base; // 4행에 정의된 객체 생성(생성자 이용)
base.shownum();
AAA& obj1 = base.createinitobj(3); // 11행의 함수를 거쳐 21행의 private 생성자를 이용해 객체 생성
obj1.shownum();
AAA& obj2 = base.createinitobj(20);
obj2.shownum();
delete& obj1;
delete& obj2;
return 0;
}
힙에 할당된 메모리 공간은 변수로 간주하여, 참조자를 통한 참조가 가능하다
소멸자
- 객체소멸시 반드시 호출되는 것은 소멸자.
- 클래스의 이름 앞에 '~'가 붙은 형태
- 반환형 X, 실제로 반환 X
- 매개변수는 void형으로 선언, 오버로딩 및 디폴트 값 설정 불가.
AAA라는 클래스가 정의되어 있다면, 아래가 소멸자.
~AAA() {...}
- 소멸자는 객체소멸 과정에서 자동으로 호출.
- 프로그래머가 직접 소멸자를 정의하지 않으면, 디폴트 생성자와 마찬가지로 아무런 일도 하지 않는 디폴트 소멸자가 자동으로 삽입.
- 대게 생성자에서 할당한 리소스의 소멸에 사용.
- 생성자 내에서 new 연산자를 이용해서 할당해 놓은 메모리 공간이 있으면, 소멸자에서 delete 이용, 메모리 소멸
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
char* name;
int age;
public:
Person(const char* myname, int myage)
{
int len = strlen(myname) + 1;
name = new char[len];
strcpy(name, myname);
age = myage;
}
void showinfo() const
{
cout << "이름 : " << name << endl;
cout << "나이 : " << age << endl;
}
~Person()
{
delete[]name;
cout << "call destructor!" << endl;
}
};
int main(void)
{
Person man1("하이맨", 30);
Person man2("하이우먼", 30);
man1.showinfo();
man2.showinfo();
return 0;
}
참고 : [윤성우 열혈 C++ 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.
'Language&Framework&Etc > C++' 카테고리의 다른 글
C/C++ 내용 정리 (0) | 2021.01.09 |
---|---|
구조체 및 클래스 간단히 정리(C와 C++) (0) | 2021.01.09 |
클래스의 완성(04-3) 생성자(Constructor)와 소멸자(Destructor)-1 (0) | 2020.12.25 |
클래스의 완성(04-2) 캡슐화(Encapsulation) (0) | 2020.12.25 |
클래스의 완성(04-1) 정보 은닉(Information Hiding) (0) | 2020.12.24 |