title: “C++ 7.함수와 클래스(객체)” categories:

  • CspecialCharspecialChar

    #C++ 7.함수와 클래스(객체) : 네이버 블로그

참조란.. 선언된 변수의 별명입니다

즉 참조 변수가 가리키는 대상은 원본 변수입니다

또 참조 변수는 초기화로 지정된 원본 변수의 공간을 공유합니다

​

그렇다면 이 참조 변수는 어디에 쓰이느냐

​

함수에 있어 인자의 전달 방식은 값에 의한 호출과 주소에 의한 호출로 나뉩니다

​

값에 의한 호출은 실인자 값이 함수의 매개 변수로 복사되어 전달되는 방식이고

주소에 의한 호출은 주소가 함수의 매개 변수(포인터)로 들어감으로써 매개 변수가 직접 전달됩니다

​

예시를 들어보자면

int add(int a, int b){

…}

위 함수에서의 인자는 int a와 int b입니다

이들은 int형 a와 b의 값을 가져옵니다

​

​

int add(int *a, int *b){

…

}

위 함수에서의 인자는 int*a와 int *b입니다

*은 포인터 타입 변수이므로 포인터 타입의 변수를 인자로 사용됩니다

즉 주소가 입력되어 add함수 안에서는 주소를 통해 인자로 사용됩니다

​

​

값에 의한 호출과 주소에 의한 호출의 차이를 아시겠나요?

자 이제 C++에 맞춰 객체를 인자로 전달해봅시다

​

name이라는 클래스가 있다고 합시다

그리고 name_size라는 함수가 있다고 합시다

​

name_size(name);를 하게 된다면 name의 값이 name_size 함수로 전달되겠지요

그렇게 값에 의한 호출이 됩나다

​

중요한 점은 값에 의한 호출을 할 때에는 매개 변수 객체의 생성자는 실행되지 않고 소멸자만 실행된다는 점이다

즉 객체가 값에 의해 전달될 때 객체가 복사된다. 복사된 객체는 새로이 만들어 진 것이 아니기 때문에 생성자가 실행되지 않는다. 하지만 복사된 객체는 소멸할 때 소별자가 생성된다

​

#include
#include
using namespace std;
class Circle {
 int radius;
public:
 Circle(int r) {
 cout << "생성자 실행" << endl;
 this->radius = r;
 }
 ~Circle() {
 cout << "소멸자 실행" << endl;
 }
 int getRadius() {
 return radius;
 }
 void setRadius(int r) {
 this->radius = r;
 }
};
void increase(Circle c) {
 int r = c.getRadius();
 c.setRadius(r + 1);
}
int main() {
 Circle waffle(30);
 increase(waffle);
}

위 코드를 실행시켜보자

​

실행 결과부터 말하자면

​

생성자 실행

소멸자 실행

소멸자 실행

​

이렇게 출력됩니다

이상하지 않나요? 왜 생성자는 한개고 소멸자는 두개일까요

​

이유는 전달 과정에 있습니다

코드를 보면서 설명하자면

Circle waffle(30); //Circle 클래스 waffle 객체 생성(생성자 실행)

​

increase(waffle); //waffle 객체를 인자로 받음으로서 복사 객체 생성

//함수 종료로 복사 객체 소멸

​

//프로그램이 종료되며 소멸자 실행

​

waffle을 인자로 받을 때 새로운 Circle 객체가 생성되는 것이 아닌 기존의 객체를 복사하기 때문에 생성자가 실행되지 않습니다.

​

​

다시 정리하면 함수 인자로 받은 객체는 복사된 객체이기 때문에 클래스 생성자가 생성되지 않고 객체만 복사됩니다. 함수가 끝나면 복사된 객체는 소멸하기에 소멸자가 실행됩니다

​

​

​

​

​

클래스 객체를 값에 의해 호출했으니 이번엔 주소에 의해 호출해봅시다

​

위의 소스를 바꿔 주소에 의한 호출로 변경했습니다

void increase(Circle \*c) { //Circle 클래스 형식의 주소를 인자로 받는다
 int r = c->getRadius(); //c는 포인터이므로 포인터 연산자 ->를 사용한다
 c->setRadius(r + 1);
}
int main() {
 Circle waffle(30); 
 increase(&waffle); Circle \*c형식을 받기 때문에 waffle의 주소를 전달한다
}

함수의 인자가 주소이기 때문에 클래스가 자체가 전달됩니다

​

즉 함수에 전달된 인자는 객체의 주소(원본)이기 때문에 함수 내에서 객체가 변경되면 함수 밖에서도 변경됩니다

​

​

이렇게 객체를 인수로 주소를 통해 복사하는 방식을 얕은 참조라 합니다

왜냐면 함수 내에서 객체의 맴버를 변경하면 함수 밖의 원본도 바뀌기 때문이죠

​

그렇다면 객체 a와 똑같은 맴버를 가진 객체 b를 만들기 위해서는 어떻게 해야 할까요?

​

우선 복사 생성자에 대해 알아야 합니다

복사 생성자란 객체를 만드는 생성자가 입력받는 값이 객체인 생성자입니다

즉 Circle 생성자가 있고

객체 A가 있다고 합시다

​

Circle B(A);는 A를 입력 인자로 갖는 생성자가 실행되어 A의 인자를 B로 옮기게 됩니다

​

아래 코드를 보면

#include
using namespace std;
class Circle {
 int radius;
public:
 Circle() {
 radius = 1;
 }
 Circle(int r) {
 this->radius = r;
 }
 Circle(Circle& c) {
 this->radius = c.radius;
 }
 double getArea() {
 return radius \* radius\*3.14;
 }
};
int main() {
 Circle a(10);
 Circle b(a);
 cout << "a(10)의 넓이 " << a.getArea()<<endl;
 cout << "b(a)의 넓이 " << b.getArea()<<endl;
}

객체에 추가된 코드는

Circle(Circle& c) {

this->radius = c.radius;

}

​

입니다

Circle() 은 생성자인데 인수를 Circle&로 받으므로 복사 생성자가 됩니다

Circle(Circle& c) c를 인자로 받고

c.radius를 현재 객체의 radius로 옮깁니다

​

​

사실 이러한 복사 생성자는 입력하지 않아도 컴파일러가 객체를 생성할 때 생성자가 객체를 인자로 받는다면 자동으로 실행됩니다

​

즉 객체를 생성할 때 객체를 인자로 받으면 자동으로 “디폴트 복사 생성자”가 만들어지고

인자로 받은 객체의 맴버를 새로 만든 객체로 옮깁니다

​

중요한 점은 디폴트 복사 생성자는 얕은 복사로 복사되고 맴버의 값을 그대로 옮기다는 점입니다

문제는 변수가 포인터 타입인 경우 포인터를 그대로 복사 하기 때문에 한쪽의 값이 변경되거나 메모리가 반환된다면 다른 한 쪽에서는 문제가 생기게 됩니다

​

그래서 객체에 포인터 변수 타입이 있을 경우에는 깊은 복사를 해야 합니다

깊은 복사를 위해서는 객체의 맴버 중 포인터 타입인 변수를 새로운 메모리를 할당하고 거기에 값을 복사해야 합니다

​

복사를 하는 인자가 무엇이냐(포인터)에 신경쓰면 될 듯 합니다

​

​

​

​

​

​

​

​

​

​

​

​

​

​

업데이트: