시험 공부용으로 시험범위와 관련한 5문제를 풀이한다.
함수의 인자 전달 방식_값에 의한 호출
- 함수를 호출하는 쪽에서 객체를 전달하기 떄문에 객체의 이름만 사용할 뿐 원본은 바뀌지 않는다.
- 호출하는 쪽의 객체가 매개 변수 객체에 그대로 복사된다.
- 매개 변수 객체의 생성자는 호출되지 않고 소멸자만 호출된다. -> 호출되는 순간 실인자 객체 상태를 매개 변수 객체에 그대로 전달하기 위함이다.
다음은 [값에 의한 호출] 시 매개 변수의 생성자가 실행되지 않는 코드이다.
#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;
}
우선 increase 이전의 코드는 생성자를 이용한 class 구현부이므로 넘어간다. 우선 main 함수에서 waffle 객체를 반지름 30으로 설정 후 생성한다. 그러면 첫 번째로 waffle 생성자가 실행된다. 이후 increase 함수를 생성하면 waffle의 내용이 그대로 c에 복사가 되어 함수에 따라 31이 된다. 그러나 c의 생성자는 실행되지 않기 때문에 코드를 실행하면 생성자 없이 바로 소멸자부터 실행된다. 이후 waffle.getRadius()의 값인 30이 실행된 후 마지막으로 waffle이 소멸한다.
함수의 인자 전달 방식_주소에 의한 호출
- 함수 호출 시 객체의 주소만 전달한다.
- 함수의 매개 변수는 객체에 대한 포인터 변수로 선언한다.
- 함수 호출 시 생성자와 소멸자가 실행되지 않는 구조이다.
다음은 앞의 값에 의한 호출의 코드를 주소에 의한 호출로 바꾼 코드이다.
void increase(Circle *c) {
int r = c->getRadius();
c->setRadius(r+1);
}
int main() {
Circle waffle(30);
increase(&waffle);
cout << waffle.getRadius() << endl;
}
우선 main 함수에서 반지름 30으로 초기화된 Circle 객체 waffle을 생성한 후 increase 함수에 waffle의 주소를 전달한다. 이는 주소를 전달하기 때문에 원본이 31로 바뀌게 된다. 앞서 말했듯이 생성자와 소멸자가 실행되지 않는 구조이기 때문에 waffle의 생성자와 소멸자만 실행되고 increase는 실행되지 않는다.
참조 변수
- 이미 존재하는 변수에 다른 이름을 선언한다.
- 참조 변수는 이름만 생기며 새로운 공간을 할당하지 않는다.
- 초기화로 지정된 기존 변수를 공유한다.
참조 변수 선언 및 사용 사례
int n = 2;
int &refn = n;
refn = 3; //refn은 n에 대한 별명이다.
다음은 기본 타입 변수에 대한 참조 코드이다.
#include <iostream>
using namespace std;
int main() {
cout << "i" << '\t' << "n" << '\t' << "refn" << endl;
int i = 1;
int n = 2;
int &refn = n;
n = 4;
refn++;
cout << i << '\t' << n << '\t' << refn << endl;
refn = i;
refn++;
cout << i << '\t' << n << '\t' << refn << endl;
int* p = &refn;
*p = 20;
cout << i << '\t' << n << '\t' << refn << endl;
}
순서대로 다음과 같다.
- 참조 변수 refn을 선언한 뒤, 이를 n이라 한다. 이후 n을 4로 선언하고 ++해주면 refn과 n은 5가 된다.
- refn을 i라 한다. 그러면 i의 값은 여전히 i이고, refn과 n만 값이 1로 초기화된다. 이후 refn을 ++하면 refn과 n은 4가 된다.
- p는 refn, 즉 n의 주소값을 가진다. (=2의 주소) 이후 *p를 20으로 바꿔 주면 refn과 n은 20이 되고 i는 그대로 1이다.
다음은 객체에 의한 참조 코드이다.
#include <iostream>
using namespace std;
class Circle {
private:
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; }
};
int main() {
Circle circle;
Circle &refc = circle;
refc.setRadius(10);
cout << refc.getArea() << " " << circle.getArea() << endl;
Circle* cp = &refc;
cp->setRadius(30);
cout << refc.getArea() << " " << circle.getArea() << endl;
}
순서대로 다음과 같다.
- 참조 변수 refc를 선언한다. 이는 circle의 별명이다.
- setRadius 함수를 사용하여 refc의 반지름을 10으로 초기화한다. 이 과정에서 circle 역시 10으로 바뀐다.
- 포인터 cp는 refc의 주소값을 담는다. cp를 30으로 초기화한다. 이 과정에서 circle 역시 30으로 바뛴다.
참조 리턴
- 변수 등과 같이 현존하는 공간에 대한 참조 리턴이다.
- 변수의 값을 리턴하는 것이 아니다.
다음은 간단한 참조 리턴 사례이다.
#include <iostream>
using namespace std;
char& find(char s[], int index) {
return s[index];
}
int main() {
char name[] = "Mike";
cout << name << endl;
find(name, 0) = 'S';
cout << name << endl;
char& ref = find(name, 2);
ref = 't';
cout << name << endl;
}
순서대로 다음과 같다.
- s[index] 공간의 참조 리턴이 이루어진다.
- ref는 name[2], 즉 'k'의 자리를 참조한다.
- ref를 t로 바꾼다.
복사 생성자
- 객체의 복사 생성 시 호출되는 특별한 생성자이다.
- 한 클래스에 오직 한 개만 선언 가능하다.
- 복사 생성자는 보통 생성자와 클래스 내에 중복 선언이 가능하다.
다음은 깊은 복사 생성자를 이용한 코드이다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class Person { // Person 클래스 선언
char* name;
int id;
public:
Person(int id, const char* name); // 생성자
Person(const Person& person); //복사 생성자
~Person(); // 소멸자
void changeName(const char* name);
void show() { cout << id << ',' << name << endl; }
};
Person::Person(int id, const char* name) // 생성자
{
this->id = id;
int len = strlen(name); // name의 문자 개수
this->name = new char[len + 1]; // name 문자열 공간 할당
strcpy(this->name, name); // name에 문자열 복사
}
Person::Person(const 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) // 만일 name에 동적 할당된 배열이 있으면
delete[] name; // 동적 할당 메모리 소멸
}
void Person::changeName(const char* name) // 이름 변경
{
if (strlen(name) > strlen(this->name))
return; // 현재 name에 할당된 메모리보다 긴 이름으로 바꿀 수 없다.
strcpy(this->name, name);
}
int main() {
Person father(1, "Kitae"); // 1. father 객체 생성
Person daughter(father); // 2. daughter 객체 복사 생성. 복사생성자 호출
cout << "daughter 객체 생성 직후 ----" << endl;
father.show(); // 3. father 객체 출력
daughter.show(); // 3. daughter 객체 출력
daughter.changeName("Grace"); // 4. daughter의 이름을 "Grace"로 변경
cout << "daughter의 이름을 Grace로 변경한 후 ----" << endl;
father.show(); // 5. father 객체 출력
daughter.show(); // 5. daughter 객체 출력
return 0; // 6, 7 daughter, father 객체 소멸
}
'코딩 스터디' 카테고리의 다른 글
[250204] C 스터디 3주차 (0) | 2025.02.04 |
---|---|
[250128] C 스터디 2주차 (0) | 2025.01.29 |
[241119] C++ 스터디 4주차 (0) | 2024.11.20 |
[241112] C++ 스터디 3주차 (1) | 2024.11.12 |
[241104] C++ 스터디 2주차 (0) | 2024.11.04 |