접근 제어자 심화
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 35번째.
이제 세 가지 접근 지정자를 이해했으므로, 동일한 클래스의 객체가 여러 개 있을 때 이들이 어떻게 동작하는지 살펴보겠습니다. 흔한 오해 중 하나는 private이 "이 객체만"을 의미한다는 것이지만, 실제로는 "이 클래스만"을 의미합니다.
동일한 클래스의 객체는 서로의 private 멤버에 접근할 수 있습니다:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// 다른 객체의 private 멤버에 접근 - 허용됨!
std::cout << other.secret;
}
};이것이 가능한 이유는 액세스 제어가 객체 수준이 아닌 클래스 수준에서 적용되기 때문입니다. readSecret 메서드는 Person 클래스의 일부이므로, 모든 Person 객체의 private 멤버에 접근할 수 있습니다. 이러한 설계 덕분에 복사 생성자나 비교 연산자와 같은 연산이 다른 객체의 내부 상태와 함께 작동할 수 있습니다.
클래스 내에서 접근 지정자를 여러 번 전환할 수도 있습니다:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // 다시 public으로
private:
void privateHelper(); // 다시 private으로
};이것이 유효한 C++이기는 하지만, 가독성을 위해 일반적으로 액세스 수준별로 멤버를 그룹화하는 것이 더 좋습니다. 대부분의 스타일 가이드는 public 멤버를 먼저 나열할 것을 권장하며(이들이 클래스 인터페이스를 형성하기 때문입니다), 그 뒤에 protected, 그 다음에 private 순으로 나열합니다.
챌린지
쉬움동일한 클래스의 객체들이 서로의 private 멤버에 어떻게 접근할 수 있는지 보여주는 지갑 비교 시스템을 만들어 보겠습니다. 이는 C++ 접근 제어에 대한 핵심적인 통찰입니다. 즉, 접근 제어는 객체 수준이 아니라 클래스 수준에서 강제됩니다.
코드를 구성하기 위해 두 개의 파일을 생성합니다:
Wallet.h: 비공개 금융 데이터를 저장하지만 지갑 간 비교를 허용하는Wallet클래스를 정의합니다. 클래스에는 다음이 포함되어야 합니다:- Private 멤버:
ownerName(string) 및balance(double) - 소유자 이름과 초기 잔액을 인자로 받는 생성자
- 소유자의 이름을 반환하는 public 게터
getOwnerName() - 이 지갑의 잔액을 다른 지갑의 private 잔액과 비교하여, 이 지갑에 돈이 더 많으면
true를 반환하는hasMoreThan(const Wallet& other)메서드 - 두 지갑의 private 잔액 합계를 반환하는
combinedBalance(const Wallet& other)메서드 - 이 지갑에서 다른 지갑으로 돈을 옮기는
transferTo(Wallet& other, double amount)메서드 — 두 지갑의 private 잔액을 직접 수정해야 합니다 (금액이 양수이고 이 지갑의 잔액을 초과하지 않는 경우에만).
- Private 멤버:
main.cpp: 객체 간 private 멤버 접근을 시연합니다. 입력으로부터 두 명의 소유자 이름과 두 개의 잔액을 읽어옵니다 (네 줄: name1, balance1, name2, balance2), 그 다음:- 입력된 값으로 두 개의
Wallet객체를 생성합니다. combinedBalance()를 사용하여"Combined wealth: "Combined wealth: $를 출력합니다."lt;amount>" - 지갑을 비교하고 잔액이 더 높은 소유자에 대해
"<name> has more money"를 출력합니다 (이를 결정하기 위해hasMoreThan()을 사용하세요). transferTo()를 사용하여 첫 번째 지갑에서 두 번째 지갑으로100.0을 이체합니다."After transfer:"를 출력합니다."Combined wealth: "Combined wealth: $를 다시 출력합니다 (변경되지 않아야 합니다)."lt;amount>" - 다시 비교하여
"<name> has more money"를 출력합니다 (결과가 바뀌었을 수 있습니다).
- 입력된 값으로 두 개의
여기서 핵심 개념은 hasMoreThan() 및 transferTo()와 같은 메서드가 다른 Wallet 객체의 private 멤버인 balance에 직접 접근할 수 있다는 것입니다. 게터가 필요하지 않습니다. 이는 C++의 접근 제어가 클래스 기반이기 때문에 가능합니다. 즉, Wallet 클래스 내부의 모든 코드는 모든 Wallet 객체의 private 멤버에 접근할 수 있습니다.
<iomanip>의 std::fixed 및 std::setprecision(2)를 사용하여 모든 통화 값을 소수점 이하 두 자리까지 형식화하십시오. std::stod()를 사용하여 잔액 입력을 변환하십시오.
치트 시트
C++의 접근 제어는 객체 수준이 아니라 클래스 수준에서 적용됩니다. 즉, 같은 클래스의 객체들은 서로의 private 멤버에 접근할 수 있습니다:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// 다른 객체의 private 멤버에 접근 - 허용됨!
std::cout << other.secret;
}
};readSecret 메서드는 Person 클래스의 일부이기 때문에 모든 Person 객체의 private secret 멤버에 접근할 수 있습니다. 이러한 설계는 복사 생성자나 비교 연산자와 같은 연산을 가능하게 합니다.
클래스 내에서 접근 지정자를 여러 번 전환할 수 있습니다:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // 다시 public으로
private:
void privateHelper(); // 다시 private으로
};하지만 가독성을 위해 접근 수준별로 멤버를 그룹화하는 것이 더 좋은 관습입니다. 대부분의 스타일 가이드에서는 public 멤버를 먼저 나열하고, 그 다음에 protected, 마지막으로 private 멤버를 나열할 것을 권장합니다.
직접 해보기
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// 입력 읽기: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// 통화 값에 대한 출력 형식 설정
cout << fixed << setprecision(2);
// TODO: 입력된 값으로 두 개의 Wallet 객체 생성
// TODO: combinedBalance()를 사용하여 합산된 자산 출력
// 형식: "Combined wealth: $<amount>"
// TODO: hasMoreThan()을 사용하여 지갑을 비교하고 누가 더 많은 돈을 가졌는지 출력
// 형식: "<name> has more money"
// TODO: transferTo()를 사용하여 첫 번째 지갑에서 두 번째 지갑으로 100.0 이체
// TODO: "After transfer:" 출력
// TODO: 합산된 자산을 다시 출력
// TODO: 다시 비교하여 누가 더 많은 돈을 가졌는지 출력
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.