출처 :
- 명품 C++ Programming (저자 황기태)
- 객체지향프로그래밍
- 노션 공유를 원하시는 분은 댓글에 이메일 남겨주세요
함수 중복
함수 중복 function overloading
- 동일한 이름의 함수가 공존 - 다형성 polymorphism
- 함수 중복이 가능한 범위
- 보통 함수들 사이
- 클래스의 멤버 함수들 사이
- 상속 관계에 있는 기본 클래스와 파생 클래스의 멤버 함수들 사이
함수 중복 성공 조건
- 중복된 함수들의 이름 동일
- 중복된 함수들의 매개 변수 타입이 다르거나 개수가 달라야 함
- 리턴 타입은 함수 중복과 무관
int sum(int a, int b, int c) {
return a + b + c;
}
double sum(double a, double b) {
return a + b;
}
int sum(int a, int b) {
return a + b;
}
// 함수 중복 실패!! - 반환형은 함수 중복과 무관하다
double sum(int a, int b) {
return (double) (a + b);
}
왜 함수 중복을 쓸까?
- 함수 호출의 편리함 동일한 이름을 사용하면 함수 이름을 구분하여 기억할 필요가 없다
- 오류 가능성 줄임 함수 호출을 잘 못하는 실수를 줄일 수 있다
int big(int a, int b) {
if (a > b) return a;
else return b;
}
int big(int a [], int size) {
int res = a[0];
for (int i = 1; i < size; i++) {
if (res < a[i]) res = a[i];
return res;
}
int main() {
int array[5] = {1, 9, -2, 8, 6};
cout << big(2,3) << endl;
cout << big(array, 5) << endl;
}
int sum(int a, int b) {
int s = 0;
for (int i = a; i < b; i++)
s += i;
return s;
}
int sum(int a) {
int s = 0;
for(int i = 0; i <= a; i++)
s += i;
return s;
}
int main() {
cout << sum(3, 5) << endl; // 12
cout << sum(3) << endl; // 6
}
생성자 함수 중복
생성자 함수 중복 목적
- 객체 생성시, 매개 변수를 통해 다양한 형태의 초깃값 전달
class string {
public:
// 빈 문자열을 가진 스트링 객체 생성
string();
// str을 복사한 새로운 스트링 객체 생성
string(string& str);
// '\\0'로 끝나는 C-스트링 s를 스트링 객체로 생성
string(char* s);
};
string str;
string address("서울시 어쩌구");
// address의 문자열을 복사한 별도의 copyAddress 생성
string copyAddress(address);
소멸자 함수 중복 불가
- 소멸자는 매개 변수를 가지지 않음
- 한 클래스 내에서 소멸자는 오직 하나만 존재
디폴트 매개 변수 default parameter
- 매개 변수에 값이 넘어오지 않는 경우, 디폴트 값을 받도록 선언된 매개 변수
- 매개변수 = 디폴트 값 형태
// a의 디폴트 값은 5
void star(int a = 5);
// a에 5 자동 전달 - star(5);와 동일
star();
// id는 디폴트 없고, text는 디폴트값 "Hello"
void msg(int id, string text="Hello");
// msg(10, "Hello"); 호출과 동일
msg(10);
// id에 20, text에 "Good morning" 전달
msg(20, "Good morning");
// 컴파일 오류, id는 반드시 값이 있어야함
msg();
// 컴파일 오류, 첫번째 매개변수 id에 값이 전달되지않음
msg("Hello");
디폴트 매개변수의 제약 조건
- 디폴트 매개변수는 보통 매개변수 앞에 선언될 수 없음
- 디폴트 매개 변수는 뒤 쪽에 몰려 선언되어야함
void calc(int a, int b=5, int c, int d=0); // 컴파일 오류
void sum(int a=0, int b, int c); // 컴파일 오류
void calc(int a, int b=5, int c=0, int d=0); // 성공
중요 예제 - 디폴트 매개 변수 가진 함수 작성
// f(); 한 줄에 빈칸 10개 출력
// f('%'); 한 줄에 % 10개 출력
// f('@', 5); 다섯 줄에 '@' 10개 출력
#include <iostream>
using namespace std;
void f(char c=' ', int line=10);
void f(char c, int line) {
for (int i = 0; i < line; i++) {
for (int j = 0; j < 10; j++)
cout << c;
cout << endl;
}
}
함수 중복 간소화
- 디폴트 매개 변수의 장점 == 함수 중복 간소화
- 중복 함수들과 디폴트 매개변수를 가진 함수는 함께 사용 불가
class Circle {
public:
Circle() { radius = 1; }
Circle(int r) { radius = r; }
// 중복된 함수와 동시 사용 불가 Circle(int r = 1) {radius = 1;}
}
class Circle {
public:
// 함수 2개를 하나로
Circle(int r = 1) { radius = r; }
}
함수 중복의 모호성
- 형 변환으로 인한 모호성
float square(float a) {}
double square(double a) {}
- 참조 매개 변수로 인한 모호성
int add(int a, int b)
int add(int a, int &b)
- 디폴트 매개 변수로 인한 모호성
void msg(int id) {}
void msg(int id, string s="") {}
교재 문제 295p
다음 함수 중복 중에서 모호한 함수 호출의 가능성이 있는 경우는?
1. void f(int a, int b); void f(int a=3, int b=3);
2. void f(int a); void f(int* a);
3. void f(int* a); void f(int& a);
4. void f(int a, int b); void f(int a=3);
static
- 변수와 함수의 생명 주기와 사용 범위를 지정하는 기억 방식의 한 종류
- 생명 주기 - 프로그램이 시작할 때 생성되고 프로그램이 종료할 때 소명
- 사용 범위 - 선언된 범위, 접근 지정에 따름
클래스의 멤버
non-static 멤버 (지금까지 봐온 멤버)
- 객체가 생성될 때 함께 생성
- 객체마다 객체 내에 생성
static 멤버
- 프로그램이 시작할 때 전역 변수로 생성
- 전체 프로그램 내에 한 번만 생성
- 클래스 당 하나만 생성, 클래스 멤버라고 불림
- 클래스의 모든 객체들이 “공유”하는 멤버
Static 멤버 선언
class Person {
public:
// non-static 멤버 선언
int money;
void addMoney(int money) { this->money += money; }
// static 멤버 선언
static int sharedMoney;
static void addShared(int n) { sharedMoney += n; }
};
// static 멤버 변수는 변수의 공간을 할당받는 선언문이 추가로 필요하다.
// 선언문은 클래스 바깥의 전역 공간에서 선언되어야 한다.
// 아래의 선언문이 없으면 링크 오류가 발생한다.
int Person::sharedMoeny = 10;
Static 멤버 사용
- private, public, protected 어떤 접근 지정도 가능
- 보통 멤버처럼 객체 이름이나 객체 포인터로 접근
int main() {
Person han;
han.money = 100;
// 객체 이름으로 static 멤버 접근
han.sharedMeney = 200;
- 클래스 이름과 범위 지정 연산자로 접근
static 멤버는 클래스 당 하나만 존재하므로 클래스 이름만으로 접근 가능하다.
// 클래스 이름으로 접근
// han.sharedMoney와 동일한 표현
Person::sharedMoney = 200;
// 당연히 non-static 멤버는 클래스명으로 접근할 수 없다.
// Person::money = 100;
static 멤버 접근 정리!
객체이름.static멤버
객체포인터->static멤버
클래스명::static멤버
static 활용
- 전역 변수나 전역 함수를 클래스에 캡슐화
- 객체 지향 언어에서 추구하는 핵심 가치가 캡슐화!
- 전역 변수나 전역 함수를 가능한 사용하지 않도록 캡슐화시키기
- 전역 변수나 전역 함수를 static으로 선언하여 클래스 멤버로 선언
- 객체 사이에 공유 변수를 만들고자 할 때
- static 멤버를 선언하여 모든 객체들이 공유
class Circle {
static int numOfCircles;
int radius;
public:
Circle(int r = 1);
~Circle() { numOfCircles--; }
double getArea() { return 3.14*radius*radius; }
static int getNumOfCircles() { return numOfCircles; }
};
Circle::Circle(int r) {
radius = r;
// 생성된 원의 개수 증가
numOfCircles++;
}
// 0으로 초기화하는 코드 반드시 써야함!
int Circle::numOfCircles = 0;
int main() {
Circle *p = new Circle[10];
// numOfCircles = 10;
delete [] p;
// numOfCircles = 0;
Circle a;
// numOfCircles = 1;
Cirlce b;
// numOfCircles = 2;
}
static 멤버 함수가 접근할 수 있는 것
- static 멤버 함수
- static 멤버 변수
- 함수 내의 지역 변수
- “non-static 멤버는 접근 불가!”
- 왜? static 멤버 함수는 프로그램이 실행될 때 만들어지므로…
- 객체가 생성되지 않은 시점에서도 static 멤버 함수가 호출될 수 있기 때문에
class PersonError {
int money;
public:
// 컴파일 오류 - static 멤버 함수는 non-static 멤버에 접근 할 수 없음
// 아직 money는 생기지도 않음
static int getMoney() { return money; }
};
int main() {
// 이때 money는 생성되지 않았다. 접근 불가능
int n = PersonError::getMoney();
}
- 반대로 non-static 멤버 함수는 static 에 접근 가능하다.
static 멤버 함수는 this 사용 불가
- static 멤버 함수는 객체가 생기기 전부터 호출 가능하므로
'[전공]' 카테고리의 다른 글
[명품 C++] 08 상속 (0) | 2022.06.06 |
---|---|
[명품 C++] 07 프렌드와 연산자 중복 (0) | 2022.06.05 |
[명품 C++] 05 함수와 참조, 복사 생성자 (6) | 2022.06.05 |
[명품 C++] 04 객체 포인터와 객체 배열, 객체의 동적 생성 (0) | 2022.06.02 |
[명품 C++] 03 - 2 생성자 소멸자 구조체 (0) | 2022.06.02 |