티스토리 뷰

1.함수의 인자 전달방법과 차이점

 

(1) 값에 의한 호출(call by value)

–함수가 호출되면 매개 변수가 스택에 생성된다.
–호출하는 코드에서 '값'을 넘겨준다.
–호출하는 코드에서 넘어온 값이 매개 변수에 '복사'된다.

(2) 주소에 의한 호출(call by address)

- 함수의 매개 변수는 포인터 타입으로 선언
- 함수가 호출되면 포인터 타입의 매개 변수가 스택에 생성된다.

- 호출하는 코드에서 넘어온 '주소' 값이 매개 변수에 저장된다.

 

(3) 참조에 의한 호출(call by reference)

- 참조 변수는 이미 선언된 변수에 대한 별명이다.

- 참조자(&)를 이용하여 선언하며, 선언 시 반드시 원본 변수로 초기화를 하여야 한다.

- 참조 변수 선언은 이름만 생성하는 것이다. 별도의 참조 변수를 위한 공간은 할당 되지 않는다.

- 대신에 원본 변수의 공간을 공유 한다.

 

*참조 변수 사용 형식

int n = 2;

int &refn = n;

 

Circle circle;

Circle &redfc = circle;

 

*참조 변수 사용 시 주의사항

- 반드시 초기화를 해주어야 한다.

- 참조자(&)는 자료형 타입과 변수 이름 사이의 어느 위치에 있어도 무관하다.

- 참조 변수 배열을 만들 수 없다.

- 참조 변수에 대한 변수 선언이 가능하다.

 

*참조 변수 사용 장점

- 참조 매개 변수를 사용하면 간단히 변수를 넘겨주기만 하면 된다.

- 함수 내에서 보통변수처럼 사용 하기 때문에 작성이 쉽고 가독성도 좋다.

 

 

2.값에 의한 호출의 문제점과 주소에 의한 호출의 비교 예제(소스코드, 실행결과에 대한 설명)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <cstdlib>
#include <iostream>
 
using namespace std;
//객체배열 생성, 소멸 예제 
class Circle {
    int radius; 
public:
    Circle(); 
    Circle(int r);
    ~Circle();
    void setRadius(int r) { radius = r; }
    int getRadius(){return radius;}
    double getArea() { return 3.14*radius*radius; }
};
 
Circle::Circle() {
    radius = 1;
    cout << "생성자 실행 radius = " << radius << endl;
}
 
Circle::Circle(int r) {
    radius = r; 
    cout << "생성자 실행 radius = " << radius << endl;
}
 
Circle::~Circle() {
    cout << "소멸자 실행 radius = " << radius << endl;
    system("PAUSE");
}
 
void increase(Circle c){ //이 함수 내에서 외부와는 독립적으로 값을 변경. 함수 내에서 값만 바뀐다.  
    int r = c.getRadius();
    c.setRadius(r+1);
}
 
void increase2(Circle *c){ //주소값을 넘겨 받았기 때문에 main함수에서 생성된 waffle객체의 값이 변경된다. 
    int r = c->getRadius();
    c->setRadius(r+1);
}
 
int main(int argc, char *argv[])
{
    //값에 의한 호출 
    Circle waffle(30);
    increase(waffle);//값을 넘겨 1 증가.  
    cout << waffle.getRadius() << endl;//30으로 출력된다. 
    
    //주소에 의한 호출 
    increase2(&waffle);//주소를 넘겨 1 증가.  
    cout << waffle.getRadius() << endl;//31 로 출력된다. 
     
    system("PAUSE");
    return EXIT_SUCCESS;
}
cs

 

<실행 결과>

 


3. 참조매개변수의 활용 예(소스코드, 실행결과에 대한 설명)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
 
using namespace std;
bool average(int a[], int sizeint &avg) {//참조 매개 변수를 이용하는 함수.
    if (size <= 0) {
        return false;
    }
    int sum = 0;
    for (int i = 0; i < size; i++)
        sum += a[i];
    avg = sum / size;
    return true;
}
 
int main() {
    int x[] = { 0,1,2,3,4,5 };
    int avg;
 
    if (average(x, 6, avg))//변수 avg를 함수의 참조 매개 변수에 넘긴다.
        cout << "평균은 " << avg << endl;//같은 공간을 공유 하기 때문에 avg에 값이 할당된다.
    else
        cout << "매개 변수 오류" << endl;
    
    if (average(x, -2, avg))//size가 0보다 작기 때문에 false가 리턴되었다.
        cout << "평균은 " << avg << endl;
    else
        cout << "매개 변수 오류" << endl;
 
}
cs

 

<실행 결과>

 

4. 객체의 복사과정

*복사 생성자(객체복사)

- 객체의 복사 생성시 호출되는 특별한 생성자이다.

- 한 클래스에 오직 한 개만 선언 가능하다.
- 복사 생성자는 보통 생성자와 클래스 내에 중복 선언 가능하다.
- 클래스에 대한 참조 매개 변수를 가지는 생성자이다.

- 복사 생성자가 선언되어 않았는데 생성문을 사용한 경우 묵시적으로 디폴트 복사 생성자를 삽입한다.

 

 

*사용 형식

Circle src(30);

Circle dest(src);

 

*복사 생성자 형식

Circle :: Circle(Cricle &c){

this->radius = c.radius;

}

복사 과정은 얕은 복사깊은 복사 두가지로 나뉜다.

 

*얕은 복사

- 원본의 포인터만 복사해서 실행한다. 즉 메모리 주소를 공유한다.

- 컴파일러가 삽입하는 디폴트 복사생성자가 얕은 복사이다.

- 컴파일러가 삽입하는 디폴트 복사생성자는 원본 객체의 사본(this)에 일대일로 복사된다.

- 메모리 주소를 공유하기 때문에 하나를 바꾸면 다른 하나도 바뀐다.

 

*깊은 복사

- 포인터 뿐만 아니라 원본 객체의 모든 내용까지 가져간다.

- 즉 메모리 주소까지 복사하기 떄문에 서로 독립적으로 실행된다.

- 깊은 복사를 위해서 복사생성자를 구현 할 때, 복사객체의 멤버를 위한 공간을 따로 할당한다.

 

*깊은 복사 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<iostream>
#include<cstring>
using namespace std;
 
class Person {
    char* name;
    int id;
public :
    Person(int id, char* name);
    Person(Person& person);
    ~Person();
    void changeName(char* name);
    void show() { cout << id << ',' << name << endl; }
};
Person::Person(int id, char* name) {//보통 생성자
    this->id = id;
    int len = strlen(name);
    this->name = new char[len + 1];
    strcpy(this->name, name);
}
 
Person::Person(Person& person) {//복사 생성자, 깊은 복사를 한다.
    this->id = person.id;//id 값 복사
    int len = strlen(person.name);//name의 문자 갯수 할당
    this->name = new char[len + 1];//name 공간 할당.
    strcpy(this->name, person.name);//name 복사
    cout << "복사 생성자 실행. 원본 객체의 이름" << this->name << endl;
}
Person :: ~Person() {
    if (name)
        delete[] name;
}
void Person::changeName(char* name) {
    if (strlen(name) > strlen(this->name))//현재 이름보다 긴 이름으로 변경 불가
        return;
    strcpy(this->name, name);
}
 
 
int main() {
    Person father(1"kitae");//보통 생성자로 객체 생성
    Person dauther(father);//복사 생성자로 father 객체를 복사 생성
 
    cout << "dauther 객체 생성 직후 ----" << endl;
    father.show();
    dauther.show();
 
    dauther.changeName("Grace");//dauther 객체의 이름을 grace로 변경.
    cout << "dauther 이름을 Grace라고 변경 한 후 -----" << endl;
    father.show();
    dauther.show();
 
    return 0;
}
cs

 

<실행 결과>

 

*복사 과정

보통 객체 생성(father)          →         얕은 복사(메모리 주소 공유되게 구현)          →        복사 객체 생성(dauther)

               →         깊은 복사(메모리 주소까지 복사되게 구현) 

댓글