[전공]

[명품 C++] 6장 함수 중복과 static 멤버

danhan 2022. 6. 5. 03:40

출처 :

  • 명품 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 멤버 함수는 객체가 생기기 전부터 호출 가능하므로