C++/summary

초기화 리스트 && 복사 생성자. && 디폴트 멤버 함수,.

gandus 2010. 6. 3. 11:44

초기화 리스트

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장에서 직접적으로 다뤄 진다.