초기화 리스트
Car(int s, int g, string c) : speed(s), gear(g), color(c) // 멤버 변수에 초기값을 넣어준다.
{
// 만약 더 하고 싶은 초기화가 있다면 여기에
}
왜 쓰냐???
- 상수 멤버를 초기화 시킬때.
class Car
{
const int MAX_SPEED = 300; // 오류다 , 멤버 변수에는 초기화 불가능하다.
int speed; // 속도
}
class Car
{
const int MAX_SPEED;
int speed; // 속도
public:
Car()
{
MAX_SPEED = 300; // 컴파일 오류! 이것 또한 오류다, 일반변수랑 컨스트랑 구별을 위해서.
}
}
그래서
class Car
{
const int MAX_SPEED;
int speed; // 속도
public:
Car() : MAX_SPEED(300)
{
}
}; // 이런식으로 사용
- 레퍼런스 멤버의 초기화
class Car
{
string& alias;
int speed; // 속도
public:
Car(string s) : alias(s) // 레퍼런스도 반드시 선언시 초기화가 있어야 하므로 생성자에 초기화.
{
cout << alias << endl;
}
};
int main()
{
Car c1("꿈의 자동차");
return 0;
}
- 객체 멤버의 경우
class Point
{
int x, y;
public:
Point(int a, int b) : x(a), y(b)
{
}
};
class Rectangle
{
Point p1, p2;
public:
Rectangle(int x1, int y1, int x2, int y2) : p1(x1, y2), p2(x2, y2) // 멤버 변수를 초기화 할때, 생성자를 이용하여 초기화 해야하는데
{ // 생성자에서 다른 생성자를 초기화를 할때 사용한다.
}
};
______________________________________________________________
복사 생성자.
복사 생성자?
새로운 객체를 만들때 기존 객체를 전달받아서 새로운 객체를 만들어 낸다.
자동으로 디폴트 복사 생성자가 생성된다.
자신과 같은 타입의 객체를 매개 변수로 받는다.
Car(Car& obj);
Car(const Car& obj); // 레퍼런스를 이용하여서 복사를 받는다.
실질적인 예제.
class Car {
int speed; // 속도
int gear; // 기어
string color; // 색상
public:
// 첫 번째 생성자
Car(int s, int g, string c) {
speed = s;
gear = g;
color = c;
}
void printInfo()
{
cout << "속도: " << speed << endl;
cout << "기어: " << gear << endl;
cout << "색상: " << color << endl;
}
};
int main()
{
Car c1(0, 1, "yellow");
Car c2(c1); // 복사 생성자 호출 - > 내가 만들지 않아도 자동으로 만들어 준다
- > 하지만 완전하지 않기 때문에 불안정하다. - > 얕은 복사 - 1:1 복사일 뿐이다.
c1.printInfo();
c2.printInfo();
return 0;
}
얕은 복사??
class Student {
char *name; // 이름 - > 포인터 변수가 있다.
int number;
public:
// 첫 번째 생성자
Student(char *pn, int n) {
name = new char[strlen(pn)+1]; // 동적 할당.
strcpy(name, pn); - > 두개를 합해서 깊은 복사이다.
number = n;
}
~Student() {
delete [] name; // 반납.
}
void setName(char *pn)
{
delete[] name;
name = new char[strlen(pn)+1];
strcpy(name, pn);
}
void printInfo()
{
cout << "이름: " << name << " ";
cout << "학번: " << number << endl;
}
};
int main()
{
Student s1("Park", 20100001); // park 은 힙 공간에 두고 시작 주소만 가지고 있음
Student s2(s1); // 복사 생성자 호출 - > 주소값만 가지고 있다.
// 힙 공간에는 park 만 존재한다., 그리고 소멸자 생성되면서 사라진 데이타를 참조한다.// park 은 사라짐.
s1.printInfo(); // s2도 소멸 하려 하지만, 존재하지 않는다.
s2.printInfo();
s1.setName("Kim");
s1.printInfo();
s2.printInfo();
return 0;
}
기억 공간을 공유하는데에서 문제가 발생한다.
해결 하려고 깊은 복사를 사용한다.
깊은 복사.
Student(Student& s) { // 레퍼런스를 이용한다.
name = new char[strlen(s.name)+1];
strcpy(name, s.name);
number = s.number;
}
깊은 복사를 하는 예제는 외워서 시험칠대 그대로 써야한다
복사 생성자가 호출되는 경우
- 기존의 객체의 내용을 복사하여서 새로운 객체를 만드는 경우
- 객체를 값으로 매개 변수로 전달하는 경우
- 객체를 값으로 반환하는 경우
복사 생성자는 언제 사용??? 함수에서 객체를 호출하는 경우, 함수에서 객체를 반환 하는경우
얕은 복사 깊은 복사의 차이점은? 얕은 복사는 그 변수들의 값만을 복사하고,
깊은 복사는 새로운 동적할당을 한후 , 원본의 데이타를 복사한다.
그림을 그려서 복사하면 좋다.
class Student {
char *name; // 이름
int number;
public:
// 첫 번째 생성자
Student(char *pn, int n) {
name = new char[strlen(pn)+1];
strcpy(name, pn);
number = n;
}
Student(Student& s) { // 복사 생성자.
name = new char[strlen(s.name)+1];
strcpy(name, s.name);
number = s.number;
}
~Student() { // 소멸자.
delete [] name;
}
char *getName()
{
return name;
}
int getNumber()
{
return number;
}
};
void displayStudent(Student obj) // 전역 함수, 객체를 전달해야함.
{
cout << "이름: " << obj.getName() << endl;
cout << "학번: " << obj.getNumber() << endl;
}
int main()
{
Student s1("Park", 20100001);
displayStudent(s1); // s1 실인자 전달. 전달되면서 복사가 된다.
// s1을 받아서 호출되므로, 복사 생성자에 의해서 초기화 된다.
// 함수가 사라지더라도, 깊은 복사로 인해서 값이 사라지진 않는다.
return 0;
}
- > 복사 생성자를 레퍼런스를 써야하는 이유는 레퍼런스가 아니라면, 재귀 호출이 되어서 계속된 호출이 된다.
____________________________________________
디폴트 멤버 함수
디폴트 생성자 point(){}
디폴트 소멸자 ~point(){}
디폴트 복사 생성자 point( point & p ) {}
디폴트 할당 연산자
s1= s2; ?? 가능하다. // 디폴트 할당연산자 함수. - > 얕은 복사이다. // 동적 할당에서 문제.
그래서 직접 할당연산자를 만들어야 한다.
★★★★클래스 설계시에 빅3 ★★★★
소멸자, 복사 생성자, 할당연산자 함수(Operator= ).
-> 동적 할당시에 필요하다.
.이것들은 12장에서 직접적으로 다뤄 진다.