Topic 1: 참조 변수 🔗
🎯 학습 목표
- 참조 변수의 개념과 특성을 이해할 수 있다
- 참조 변수의 선언과 초기화 방법을 익힐 수 있다
- 참조와 포인터의 차이점을 구분할 수 있다
- 참조를 활용한 효율적인 프로그래밍을 구현할 수 있다
📖 참조(Reference)란?
참조는 이미 존재하는 변수에 대한 별명(alias)입니다. 참조 변수는 원본 변수와 같은 메모리 위치를 공유합니다.
아날로지: 사람의 별명과 같습니다.
- 원본 변수: 본명 (김철수)
- 참조 변수: 별명 (철수, 수철이) - 같은 사람을 가리킴
- 포인터: 주민등록번호 - 그 사람을 찾을 수 있는 정보
🔍 참조 변수 기본 문법
#include <iostream>
using namespace std;
int main() {
int original = 100;
// 참조 변수 선언 (반드시 초기화 필요)
int& ref = original; // ref는 original의 별명
cout << "=== 참조 변수 기초 ===" << endl;
cout << "original 값: " << original << endl;
cout << "ref 값: " << ref << endl;
cout << "original 주소: " << &original << endl;
cout << "ref 주소: " << &ref << endl; // 같은 주소!
// 참조를 통한 값 변경
ref = 200;
cout << "\nref = 200 후:" << endl;
cout << "original 값: " << original << endl; // 200으로 변경됨
cout << "ref 값: " << ref << endl;
// 원본을 통한 값 변경
original = 300;
cout << "\noriginal = 300 후:" << endl;
cout << "original 값: " << original << endl;
cout << "ref 값: " << ref << endl; // 300으로 변경됨
return 0;
}
참조의 특성
#include <iostream>
using namespace std;
int main() {
cout << "=== 참조의 특성 ===" << endl;
// 1. 참조는 반드시 초기화해야 함
int value1 = 10;
int& ref1 = value1; // 올바른 초기화
// int& ref2; // 오류! 초기화 없음
// 2. 참조는 재할당 불가능
int value2 = 20;
int& ref2 = value2;
cout << "초기 상태:" << endl;
cout << "ref1 = " << ref1 << ", ref2 = " << ref2 << endl;
ref1 = ref2; // ref2의 값(20)을 ref1에 대입 (ref1이 ref2를 참조하는 게 아님)
cout << "ref1 = ref2 후:" << endl;
cout << "value1 = " << value1 << endl; // 20으로 변경됨
cout << "value2 = " << value2 << endl; // 여전히 20
cout << "ref1 = " << ref1 << endl; // 20
cout << "ref2 = " << ref2 << endl; // 20
// 3. 참조는 null이 될 수 없음
// int& nullRef = nullptr; // 오류!
return 0;
}
📋 다양한 참조 타입
const 참조
#include <iostream>
#include <string>
using namespace std;
int main() {
cout << "=== const 참조 ===" << endl;
int number = 42;
const int& constRef = number; // const 참조
cout << "number: " << number << endl;
cout << "constRef: " << constRef << endl;
number = 100; // 원본은 변경 가능
cout << "number 변경 후: " << constRef << endl;
// constRef = 200; // 오류! const 참조로는 값 변경 불가
// const 참조는 임시 객체도 참조 가능
const int& tempRef = 500; // 임시값 참조
const string& strRef = "Hello World"; // 문자열 리터럴 참조
cout << "tempRef: " << tempRef << endl;
cout << "strRef: " << strRef << endl;
return 0;
}
배열과 참조
#include <iostream>
using namespace std;
int main() {
cout << "=== 배열과 참조 ===" << endl;
int arr[5] = {1, 2, 3, 4, 5};
// 배열 전체에 대한 참조
int (&arrayRef)[5] = arr;
cout << "원본 배열: ";
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
// 참조를 통한 배열 수정
arrayRef[2] = 99;
cout << "참조로 수정 후: ";
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
// 개별 요소에 대한 참조
int& firstElement = arr[0];
int& lastElement = arr[4];
firstElement = 10;
lastElement = 50;
cout << "개별 요소 수정 후: ";
for(int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
🔄 함수와 참조
참조는 함수에서 매우 유용하게 사용됩니다.
참조 매개변수
#include <iostream>
using namespace std;
// 값에 의한 전달
void byValue(int x) {
x = 999;
cout << "byValue 내부 x: " << x << endl;
}
// 참조에 의한 전달
void byReference(int& x) {
x = 888;
cout << "byReference 내부 x: " << x << endl;
}
// 두 값 교환 함수
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 배열 요소 총합 (const 참조 사용)
int sumArray(const int (&arr)[5]) {
int total = 0;
for(int i = 0; i < 5; i++) {
total += arr[i];
// arr[i] = 0; // 오류! const 배열 수정 불가
}
return total;
}
int main() {
cout << "=== 함수와 참조 매개변수 ===" << endl;
int num = 100;
cout << "초기 num 값: " << num << endl;
byValue(num);
cout << "byValue 호출 후 num: " << num << endl; // 100 (변경 안됨)
byReference(num);
cout << "byReference 호출 후 num: " << num << endl; // 888 (변경됨)
// 두 값 교환
cout << "\n--- 값 교환 ---" << endl;
int x = 10, y = 20;
cout << "교환 전: x=" << x << ", y=" << y << endl;
swap(x, y);
cout << "교환 후: x=" << x << ", y=" << y << endl;
// 배열 합계
cout << "\n--- 배열 합계 ---" << endl;
int numbers[5] = {1, 2, 3, 4, 5};
cout << "배열 합계: " << sumArray(numbers) << endl;
return 0;
}
참조 반환
#include <iostream>
using namespace std;
// 전역 배열
int numbers[5] = {10, 20, 30, 40, 50};
// 배열 요소 참조 반환 함수
int& getElement(int index) {
if(index >= 0 && index < 5) {
return numbers[index];
}
throw "Index out of range";
}
// 배열 출력 함수
void displayArray() {
cout << "배열: ";
for(int i = 0; i < 5; i++) {
cout << numbers[i] << " ";
}
cout << endl;
}
// 전역 변수 참조 반환 (안전한 예시)
int globalVar = 1000;
int& getGlobalVar() {
return globalVar; // 안전함 (전역변수)
}
// 위험한 예시 (사용하지 말 것!)
/*
int& dangerousFunction() {
int localVar = 500;
return localVar; // 위험! 지역변수 참조 반환
}
*/
int main() {
cout << "=== 참조 반환 ===" << endl;
displayArray();
// 참조 반환을 이용한 값 수정
cout << "\n인덱스 2의 값 변경:" << endl;
getElement(2) = 999;
displayArray();
// 연쇄 대입도 가능
getElement(0) = getElement(1) = 100;
displayArray();
// 전역변수 참조 반환
cout << "\n전역변수 참조:" << endl;
cout << "전역변수 값: " << getGlobalVar() << endl;
getGlobalVar() = 2000; // 참조를 통한 수정
cout << "수정 후: " << globalVar << endl;
return 0;
}
🧪 실전 예제: 성적 관리 시스템
#include <iostream>
#include <string>
using namespace std;
// 학생 정보를 위한 전역 변수들
string studentName = "김철수";
int scores[5] = {85, 92, 78, 88, 95};
int scoreCount = 5;
// 이름 참조 반환 함수 (수정 가능)
string& getName() {
return studentName;
}
// 특정 인덱스의 점수 참조 반환
int& getScore(int index) {
if(index >= 0 && index < scoreCount) {
return scores[index];
}
throw "Score index out of range";
}
// 평균 계산 함수
double getAverage() {
if(scoreCount == 0) return 0.0;
int total = 0;
for(int i = 0; i < scoreCount; i++) {
total += scores[i];
}
return static_cast<double>(total) / scoreCount;
}
// 성적 정보 출력 함수
void displayGrades() {
cout << "\n학생: " << studentName << endl;
cout << "점수: ";
for(int i = 0; i < scoreCount; i++) {
cout << scores[i];
if(i < scoreCount - 1) cout << ", ";
}
cout << endl;
cout << "평균: " << getAverage() << endl;
}
// 최고 점수 찾기 (참조 반환)
int& findMaxScore() {
if(scoreCount == 0) {
throw "No scores available";
}
int maxIndex = 0;
for(int i = 1; i < scoreCount; i++) {
if(scores[i] > scores[maxIndex]) {
maxIndex = i;
}
}
return scores[maxIndex];
}
// 성적 향상 함수
void improveGrades(int improvement) {
cout << "\n=== 성적 향상 적용 (" << improvement << "점 추가) ===" << endl;
for(int i = 0; i < scoreCount; i++) {
int& score = getScore(i); // 참조로 받아서 직접 수정
score += improvement;
if(score > 100) score = 100; // 최대값 제한
}
}
int main() {
cout << "=== 참조를 활용한 성적 관리 시스템 ===" << endl;
// 초기 성적 출력
displayGrades();
// 특정 점수 수정 (참조 사용)
cout << "\n3번째 점수를 90점으로 수정" << endl;
getScore(2) = 90; // 0-based index
displayGrades();
// 최고 점수 찾기 및 보너스 적용
try {
int& maxScore = findMaxScore();
cout << "\n최고 점수: " << maxScore << endl;
cout << "최고 점수에 5점 보너스 적용" << endl;
maxScore += 5;
displayGrades();
} catch(const char* error) {
cout << "오류: " << error << endl;
}
// 전체 성적 향상
improveGrades(3);
displayGrades();
// 학생 이름 수정 (참조 사용)
cout << "\n학생 이름 변경:" << endl;
getName() = "김영수"; // 참조를 통한 직접 수정
displayGrades();
return 0;
}
🎮 실습 프로젝트: 배열 기반 참조 활용
#include <iostream>
#include <string>
using namespace std;
// 정수 배열 관리
int numbers[10] = {10, 20, 30, 40, 50};
int currentSize = 5;
// 배열 요소 참조 반환 (수정 가능)
int& getElement(int index) {
if(index >= 0 && index < currentSize) {
return numbers[index];
}
throw "Index out of range";
}
// 첫 번째 요소 참조 반환
int& front() {
if(currentSize == 0) {
throw "Array is empty";
}
return numbers[0];
}
// 마지막 요소 참조 반환
int& back() {
if(currentSize == 0) {
throw "Array is empty";
}
return numbers[currentSize - 1];
}
// 배열 출력 함수
void printArray() {
cout << "[";
for(int i = 0; i < currentSize; i++) {
cout << numbers[i];
if(i < currentSize - 1) cout << ", ";
}
cout << "]" << endl;
}
// 배열 요소들의 합계 (참조 사용)
int sumArray() {
int sum = 0;
for(int i = 0; i < currentSize; i++) {
sum += numbers[i];
}
return sum;
}
// 모든 요소에 값 더하기
void addToAll(int value) {
for(int i = 0; i < currentSize; i++) {
int& element = getElement(i); // 참조를 통한 직접 수정
element += value;
}
}
// 문자열 배열 관리
string words[10] = {"Hello", "World", "C++", "Reference"};
int wordCount = 4;
// 문자열 요소 참조 반환
string& getWord(int index) {
if(index >= 0 && index < wordCount) {
return words[index];
}
throw "Word index out of range";
}
// 문자열 배열 출력
void printWords() {
cout << "[";
for(int i = 0; i < wordCount; i++) {
cout << "\"" << words[i] << "\"";
if(i < wordCount - 1) cout << ", ";
}
cout << "]" << endl;
}
int main() {
cout << "=== 참조를 활용한 배열 관리 ===" << endl;
cout << "초기 배열: ";
printArray();
// 참조를 통한 요소 수정
cout << "\n=== 개별 요소 수정 ===" << endl;
getElement(2) = 99; // 3번째 요소 수정
front() = 5; // 첫 번째 요소 수정
back() = 100; // 마지막 요소 수정
cout << "수정 후: ";
printArray();
// 배열 합계
cout << "\n배열 합계: " << sumArray() << endl;
// 모든 요소에 값 더하기
cout << "\n모든 요소에 10 더하기" << endl;
addToAll(10);
printArray();
// 문자열 배열 테스트
cout << "\n=== 문자열 배열 테스트 ===" << endl;
cout << "문자열 배열: ";
printWords();
// 문자열 수정
getWord(1) = "Beautiful";
getWord(3) += "s"; // 마지막 단어에 's' 추가
cout << "수정 후: ";
printWords();
// 예외 처리 테스트
cout << "\n=== 예외 처리 테스트 ===" << endl;
try {
cout << getElement(100) << endl; // 범위 초과
} catch(const char* error) {
cout << "오류 발생: " << error << endl;
}
return 0;
}
💡 참조 vs 포인터 비교
#include <iostream>
using namespace std;
int main() {
cout << "=== 참조 vs 포인터 비교 ===" << endl;
int value = 100;
// 참조
int& ref = value;
// 포인터
int* ptr = &value;
cout << "원본 값: " << value << endl;
cout << "참조 값: " << ref << endl;
cout << "포인터 값: " << *ptr << endl;
cout << "\n=== 차이점 정리 ===" << endl;
cout << "1. 초기화:" << endl;
cout << " 참조: 선언과 동시에 반드시 초기화" << endl;
cout << " 포인터: 초기화 선택사항 (nullptr 가능)" << endl;
cout << "\n2. 재할당:" << endl;
cout << " 참조: 불가능 (평생 같은 변수 참조)" << endl;
cout << " 포인터: 가능 (다른 변수 주소로 변경)" << endl;
cout << "\n3. 산술 연산:" << endl;
cout << " 참조: 불가능" << endl;
cout << " 포인터: 가능 (포인터 산술)" << endl;
cout << "\n4. 메모리 오버헤드:" << endl;
cout << " 참조: 없음 (별명일 뿐)" << endl;
cout << " 포인터: 있음 (" << sizeof(ptr) << "바이트)" << endl;
cout << "\n5. Null 가능성:" << endl;
cout << " 참조: 불가능" << endl;
cout << " 포인터: 가능 (nullptr)" << endl;
return 0;
}
💡 모범 사례와 주의사항
✅ 좋은 습관
-
함수 매개변수로 참조 사용
// 큰 객체의 복사 비용 절약 void processLargeObject(const LargeClass& obj) { // 복사 없이 obj 사용 }
-
참조 반환 시 생명주기 확인
int& getSafeReference() { static int safeValue = 10; // 정적 변수는 안전 return safeValue; }
-
const 참조 활용
// 수정하지 않는 경우 const 사용 void printValue(const int& value) { cout << value << endl; }
❌ 피해야 할 실수
-
지역변수 참조 반환
int& dangerous() { int local = 10; return local; // 위험! 댕글링 참조 }
-
초기화되지 않은 참조
// int& ref; // 오류! 참조는 반드시 초기화
-
임시 객체에 대한 비const 참조
// int& ref = 10; // 오류! 임시값은 const 참조만 가능
Last updated on