05. 함수와 참조, 생성자

5.1 함수의 인자 전달 방식

  1. 값에 의한 호출(call by value) : 인자 값이 함수의 매개 변수에 복사되어 전달
    • 특징: 값을 복사하여 전달하므로 함수 내에서 실인자를 손상시킬 수 없는 장점이 있다.
    • 문제점: 매개 변수 객체의 생성자는 실행 X 소멸자만 실행
        void increase(Circle c){
            int r = c.getRadius();                                                
            c.setRadius(r+1)                                                       
        }
      
        int main(){
            Circle waffle(30);                       
            increase(waffle);                               // 결과
            cout << waffle.getRadius() << endl;             //  30
        }
      
  2. 주소에 의한 호출(call by address) : 주소를 직접 포인터 타입의 매개변수에 전달받는 방식
    • 특징: 인자의 주소를 넘겨주어 함수 내에서 실인자의 값을 변경하고자 할 때 이용.
    • 원본 객체를 복사하는 사간 소모가 없고, ‘값에 의한 호출’시 발생하는 생성자 소멸자 비대칭 문제가 없다.

5.3 객체 치환 및 객체 리턴

객체 치환

동일한 클래스 타입에서만 객체 치환이 가능하다.

Circle c1(5); Circle c2(30);
c1 = c2; //c2 객체를 c1 객체에 비트 단위로 복사한다. c1의 반지름은 30이 된다.

객체 리턴

Circle getCircle(){
    Circle tmp(30);
    return tmp;         // 객체 tmp 리턴
}

int main(){
    Circle c;
    c = getCircle();  // tmp읭 겍체의 복사본이 c에 치환 c의 반지름은 30이 된다.
}

객체 c가 생성될 때 radius = 1 이지만 getCircle()이 리턴한 tmp 객체로 치환되면 c의 반지름은 30이 됨
***

5.4 참조와 함수

참조 변수

참조 변수: 선언된 변수에 대한 별명이다. ‘&’를 이용하여 선언하고, 반드시 원본 변수로 초기화를 해야함

int n=2;
int &refn = n;      //초기화
Circle circle;
Circle &refc = circle;

참조 변수 사용시 주의사항

  • 참조 변수는 포인터가 아니므로 refc->setRadius(30);를 할 시 오류가 발생한다.
  • 초기화가 없다면 컴파일 오류가 발생한다.
  • 참조 변수의 배열을 만들 수 없다. char &n[10]; 컴파일 오류 발생
  • 팜조 변수에 대한 참조 선언이 가능하다. int &r = refn; 참조 변수 refn에 대한 참조 변수 r 선언 가능

참조에 의한 호출

참조에 의한 호출(call by reference) : 함수의 매개 변수를 참조 타입으로 선언하여 매개 변수가 참수를 호출하는 쪽의 실인자를 참조하여 실인자와 공간을 공유하도록하는 인자 전달 방식이다.
참조 매개 변수 : 참조 타입으로 선언된 함수의 매개 변수
참조에 의한 호출 : 참조 매개 변수를 가지는 함수

void swap(iant &a, &b){
    int tmp;

    tmp = a;
    a = b;
    b = tmp;
}
int main(){
    int m=2,n=2;
    swap(m,n);
    cout<<m<<< ' '<<n;
}

swap()함수가 호출되면, 참조 매개 변수 a, b는 실인자 m, n을 참조하도록 초기화되며, 함수 내에서는 보통 변수처럼 사용된다. 변수 m, n은 main() 스택에 생성되지만, 참조 매개 변수 a, b는 이름만 존재하며 swap()의 스택에 공간을 할당받지 않는다. swap() 함수가 실행되어 a와 b값이 교환되면, 결과적으로 m과 n의 값이 교환된다. 함수가 종료되면 a, b의 이름은 사라지고 m, n은 각각 9, 2로서 교환된 값으로 남아 있다.

  • 참조에 의한 호출의 장점 ‘주소에 의한 호출’은 &,*를 반복적으로 사용해야하기 때문에 가독성이 떨어진다. 그러나 참조 매개 변수를 사용하면 간단히 변수를 넘겨주기만 하면 되고, 함수 내에서도 참조 매개 변수를 보통 변수처럼 사용하기 때문에 작성하기 쉽고 가독성이 좋다는 장점이 있다.

참조 리턴

변수 등과 같이 현존하는 공간에 대한 참조의 리턴이다.

char c = 'a';
char &find(){
    return c;           //find()함수 내에서 char 타입의 변수 c에 대한 참조를 리턴
}
char a = find();        //참조 리턴에 대한 치환문(변수 c의 값 'a'가 변수 a에 치환된다.)
char &ref = find();     //참조 리턴에 대한 치환문(참조 변수로 참조를 리턴 받을 수 있다.)
ref = 'm';              //ref는 find()가 리턴한 변수 c의 참조가 된다. 
                        //그러므로 다음과 같이 ref에 대한 연산은 모두 변수 c에 대해 이루어지는 연산이 된다.
find() = 'b';          // c = 'b'와 동일

5.5 복사 생성자

얕은 복사

클래스를 복사할 때 객체는 복사가 되지만 포인터는 메모리가 복사가 되지 않고 공유만 되게 된다.

  • 단점 : 얕은 복사시 포인터는 메모리가 공유가 되기 때문에 사본에서 메모리의 내용을 변경하면 원본의 메모리도 변경되는 문제가 있다.

    깊은 복사

    객체는 복사가 되고 포인터의 메모리도 같이 복사가 되기 때문에 원본과 사본은 별개의 메모리를 가진다.

    1) 복사 생성자 선언

    ```c class Circle{ Circle(const Circle &c); //복사 생성자 선언
    };

Circle::Circle(const Circle &c){ //복사 생성자 구현

}

##### 2) 복사 생성자 실행  
```c
Circle src(30);                         //보통 생성자 호출
Circle dest(src);                       //src객체를 복사하여 dest 객체 생성. 복사 생성자 Circle(Circle &c) 호출
// Circle(Circle &c)가 호출될때 src객체가 참조 매개 변수 c로 전달한다. c는 곧 src이다. 
디폴트 복사 생성자 : 복사 생성자가 없어도 컴파일러가 알아서 얕은 복사로 디폴트 복사 생성자를 만들어낸다.