Search
Duplicate

5장. 함수와 참조, 복사 생성자

생성일
2022/04/06 01:55
태그
C++

함수와 참조, 복사 생성자

함수의 인자 전달 방식 리뷰

인자 전달 방식

값에 의한 호출, call by value
함수가 호출되면 매개변수가 스택에 생성된다
호출하는 코드에서 값을 넘겨준다
호출하는 코드에서 넘어온 값이 매개변수에 복사된다
int add(int a, int b) { return a * b; } add(100, 200);
C++
복사
주소에 의한 호출, call by address
함수의 매개변수는 포인터 타입이다
함수가 호출되면 포인터 타입의 매개변수가 스택에 생성된다
호출하는 코드에서는 명시적으로 주소를 넘겨준다
기본 타입 변수나 객체의 경우, 주소를 전달한다
배열의 경우, 배열의 이름
int add(int *a, int *b) { return a * b; } add(100, 200);
C++
복사
호출하는 코드에서 넘어온 주소 값이 매개변수에 저장된다
예제)
1.
값에 의한 호출
#include <iostream> using namespace std; void swap(int a, int b) { int tmp; tmp = a; a = b; b = tmp; } int main() { int m = 2, n = 9; swap(m, n); cout << m << "" << n; }
C++
복사
2.
주소에 의한 호출
#include <iostream> using namespace std; void swap(int *a, int *b) { int tmp; tmp = *a; *a = *b; *b = tmp; } int main() { int m = 2, n = 9; swap(&m, &n); cout << m << "" << n; }
C++
복사

‘값에 의한 호출' 로 객체 전달

함수를 호출하는 쪽에서 객체를 전달
객체 이름만 사용
함수의 매개변수 객체 생성
매개변수 객체의 공간이 스택에 할당
호출하는 쪽의 객체가 매개변수 객체에 그대로 복사된다
매개변수 객체의 생성사는 호출되지 않는다
함수 종료
매개변수 객체의 소멸자를 호출한다
값에 의한 호출 시 매개변수 객체의 생성자가 실행되지 않는 이유?
→ 호출되는 순가의 실인자 객체 상태를 매개변수 객체에 그대로 전달하기 위함
#include <iostream> using namespace std; class Circle { private: int radius; public: Circle(); Circle(int r); ~Circle(); double getArea() { return 3.14 * radius * radius; } int getRadius() { return radius; } void setRadius(int radius) { this->radius = radius; } }; Circle::Circle() { radius = 1; cout << "생성자 실행 radius = " << radius << endl; } Circle::Circle(int radius) { this->radius = radius; cout << "생성자 실행 radius = " << radius << endl; } Circle::~Circle() { cout << "소멸자 실행 radius = " << radius << endl; } void increase(Circle c) { int r = c.getRadius(); c.setRadius(r + 1); } int main() { Circle waffle(30); increase(waffle); cout << waffle.getRadius() << endl; }
C++
복사

함수에 객체 전달 - ‘주소에 의한 호출'로

함수 호출시 객체의 주소만 전달
함수의 매개변수는 객체에 대한 포인터 변수로 선언
함수 호출 시 생성자 소멸자가 실행되지 않는 구조이다

객체 치환 및 객체 리턴

객체 치환
동일한 클래스 타입의 객체끼리 치환이 가능하다
객체의 모든 데이터가 비트 단위로 복사
Circle c1(5); Circle c2(30); c1 = c2; //c2 객체를 c1 객체에 비트 단위 복사, c1의 반지름 30 됨
C++
복사
치환된 두 객체는 현재 내용물만 같을 뿐 독립적인 공간 유지한다
객체 리턴
객체의 복사본 리턴
Circle getCircle() { Circle tmp(30); return tmp; //객체 tmp 리턴 } Circle c; //c의 반지름 1 c = getCircle(); //tmp 객체의 복사본이 c에 치환, c의 반지름은 30이 됨
C++
복사
#include <iostream> using namespace std; class Circle { int radius; public: Circle() { radius = 1; } Circle(int radius) { this->radius = radius; } void setRadius(int radius) { this->radius = radius; } double getArea() { return 3.14 * radius * radius; } }; Circle getCircle() { Circle tmp(30); return tmp; // 객체 tmp을 리턴한다 } int main() { Circle c; cout << c.getArea() << endl; c = getCircle(); cout << c.getArea() << endl; }
C++
복사

참조 (reference)

참조란 가리킨다는 뜻으로, 이미 존재하는 객체나 변수에 대한 별명
참조활용
참조 변수
참조에 의한 호출
참조 리턴

참조 변수

참조 변수 선언
참조자 &의 도입
이미 존재하는 변수에 대한 다른 이름(별명)을 선언
참조 변수는 이름만 생기며
참조 변수에 새로운 공간을 할당하지 않는다
초기화로 지정된 기존 변수를 고유한다
int n = 2; int &refn = n; refn = 3;
C++
복사
Circle circle; Circle &refc = circle; refc.setRadius(40);
C++
복사
refc → setRadius(30); 으로 하면 안된다
#include <iostream> using namespace std; int main() { cout << "i" << '\t' << "n" << '\t' << "refn" << endl; int i = 1; int n = 2; int &refn = n; //참조변수 refn 선언, refn은 n에 대한 별명 n = 4; refn++; cout << i << '\t' << n << '\t' << refn << endl; refn = i; //refn = 1, n = 1 refn++; //refn = 2, n = 2 cout << i << '\t' << n << '\t' << refn << endl; int *p = &refn; // p는 n의 주소를 가짐 *p = 20; // refn=20, n=20 cout << i << '\t' << n << '\t' << refn << endl; }
C++
복사

참조에 의한 호출

참조를 가장 많이 활용하는 사례
call by reference 라고 부름
함수형식
함수의 매개변수를 참조 타입으로 선언
참조 매개변수 (reference parameter) 라고 부른다
참조 매개변수는 실인자 변수를 참조한다
참조 매개변수의 이름만 생기고 공간이 생기지 않음
참조 매개변수는 실인자 변수 공간을 공유한다
참조 배개변수에 대한 조작은 실인자 변수 조작 효과
#include <iostream> using namespace std; void swap(int &a, int &b) { //참조 매개변수 a, b int tmp; tmp = a; a = b; // 참조 매개변수를 보통 변수처럼 사용 b = tmp; } int main() { int m = 2, n = 9; swap(m, n); // 함수가 호출되면 m, n에 대한 참조변수 a, b 가 생긴다 cout << m << "" << n; }
C++
복사

참조 매개변수가 필요한 사례

#include <iostream> using namespace std; bool average(int a[], int size, int& avg) { // 참조 매개 변수 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)) cout << "평균은 "<< avg <<endl; else cout << "매개변수 오류" << endl; if (average(x, -2, avg)) cout << "평균은 " << avg << endl; else cout << "매개변수 오류 " << endl; }
C++
복사
예제)
#include <iostream> using namespace std; class Circle { private: int radius; public: Circle(); Circle(int r); ~Circle(); double getArea() { return 3.14 * radius * radius; } int getRadius() { return radius; } void setRadius(int radius) { this->radius = radius; } }; Circle::Circle() { radius = 1; cout << "생성자 실행 radius = " << radius << endl; } Circle::Circle(int radius) { this->radius = radius; cout << "생성자 실행 radius = " << radius << endl; } Circle::~Circle() { cout << "소멸자 실행 radius = " << radius << endl; } void increaseCircle(Circle &c) { int r = c.getRadius(); c.setRadius(r + 1); } int main() { Circle waffle(30); increaseCircle(waffle); cout << waffle.getRadius() << endl; }
C++
복사

참조 리턴

C 언어의 함수 리턴
함수는 반드시 값만 리턴
기본 타입 값 : int, char, double 등
포인터 값
C++ 의 함수 리턴
함수는 값 외에 참조 리턴 가능
참조 리턴
변수 등과 같이 현존하는 공간에 대한 참조 리턴
변수의 값을 리턴하는 것이 아니다
#include <iostream> using namespace std; char& find(char s[], int index) { return s[index]; // s[index] 공간의 참조 리턴 } int main() { char name[] = "Mike"; cout << name << endl; find(name, 0) = 'S'; //name[0] = "S" 로 변경 // find()가 리턴한 위치에 문자 'm' 저장 cout << name << endl; char& ref = find(name, 2); //ref는 name[2] 참조 ref = 't'; cout << name << endl; } // 결과 //Mike //Sike //Site
C++
복사

C++ 에서 얕은 복사와 깊은 복사

얕은 복사 (shallow copy)

객체 복사 시, 객체의 멤버를 1:1로 복사
객체의 멤버 변수에 동적 메모리가 할당된 경우
사본은 원본 객체가 할당 받은 메모리를 공유하는 문제 발생

깊은 복사 (deep copy)

객체 복사 시, 객체의 멤버를 1:1로 복사
객체의 멤버 변수에 동적 메모리가 할당된 경우
사본은 원본이 가진 메모리 크기 만큼 별도로 동적 할당
원본의 동적 메모리에 있는 내용을 사본에 복사
완전한 형태의 복사
사본과 원본은 메모리를 공유하는 문제 없음

복사 생성자

복사 생성자 (copy constructor) 란?
객체의 복사 생성시 호출되는 특별한 생성자
특징
한 클래스에 오직 한 개만 선언 가능
복사 생성자는 보통 생성자와 클래스 내에 중복 선언 가능
모양
클래스에 대한 참조 매개변수를 가지는 톡특한 생성자
복사 생성자 선언

복사 생성 과정

디폴트 복사 생성자

복사 생성자가 선언되어 있지 않는 클래스
컴파일러는 자동으로 디폴트 복사 생성자 삽입
예)
예제) 얕은 복사 생성자를 사용하여 프로그램이 비정상 종료되는 경우
#define _CRT_SECURE_NO_WARNINGS #include <cstring> #include <iostream> using namespace std; class Person { char *name; int id; public: Person(int id, const char *name); // 생성자 ~Person(); void changeName(const char *name); void show() { cout << id << ',' << name << endl; } }; Person::Person(int id, const char *namee) { // 생성자 this->id = id; int len = strlen(name); // name의 문자 개수 this->name = new char[len + 1]; // name 문자열 공간 할당 strcpy(this->name, name); // name에 문자열 복사 } Person::~Person() { if (name) // 만약 name에 동적 할당된 배열이 있으면 delete[] name; } void Person::changeName(const char *name) { // 이름 변경 if (strlen(name) > strlen(this->name)) return; strcpy(this->name, name); } int main() { Person father(1, "Kitae"); // father 객체 생성 Person daughter(father); // daughter 객체 복사 생성, 복사 생성사 호출 // 컴파일러가 삽입한 디폴트 복사 생성자 호출 cout << "daughter 객체 생성 직후 ---" << endl; father.show(); // father 객체 출력 daughter.show(); // daughter 객체 출력 daughter.changeName("Grace"); // daughter 의 이름ㅇ르 grace 로 변경 cout << "daughter 이름을 Grace로 변경한 후 ---" << endl; father.show(); daughter.show(); return 0; }
C++
복사