2023. 11. 5. 21:38ㆍ프로그래밍 공부/OOP
4.1 매개변수
call-by-value(값에 의한 호출) 매개변수(parameter): 인수의 값만 연결되는 매개변수
call-by-reference(참조에 의한 호출) 매개변수(parameter): 인수는 변수이고 변수 자체는 플러그인이므로 함수 호출을 통해 변수 값을 변경할 수 있다.
참조에 의한 호출(call-by-reference) 함수 선언은 매개변수에 & 기호를 붙인 형태로 예시는 아래와 같다.
void getInput( double& variableOne, int& variableTwo);
Call-by-Value 매개변수
대부분의 경우 call-by-value 매개 변수는 함수 호출에서 해당 인수의 값으로 채워지는 일종의 공백 또는 자리 표시자로 생각할 수 있습니다.
그러나 경우에 따라 call-by-value 매개 변수를 로컬 변수로 사용하고 함수 정의 본문 내에서 매개 변수의 값을 변경하는 것이 편리합니다.
예시)
디스플레이 4.1에서 minutesWorked라는 공식 매개변수를 변수처럼 사용하고 해당 줄에서 값을 변경하였습니다.
minutesWorked = hoursWorked*60 + minutesWorked;
디스플레이 4.1 지역 변수가 이용된 공식 매개변수
//Law office billing program.
#include <iostream>
using namespacestd;
const doubleRATE = 150.00; //Dollars per quarter hour.
double fee(int hoursWorked, int minutesWorked);
//Returns the charges for hoursWorked hours and
//minutesWorked minutes of legal services .
int main( )
{
int hours, minutes;
double bill;
cout << "Welcome to the law office of\n"
<< "Dewey, Cheatham, and Howe.\n"
<< "The law office with a heart.\n"
<< "Enter the hours and minutes"
<< " of your consultation:\n";
cin >> hours >> minutes;
bill = fee(hours, minutes);
//The value of minutes is not changed by the call to fee.
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
cout << "For " << hours << " hours and " << minutes
<< " minutes, your bill is $" << bill << endl;
return 0;
}
double fee(int hoursWorked, int minutesWorked)
{
int quarterHours;
// minutesWorked is a local variable initialized to the value of minutes.
minutesWorked = hoursWorked*60 + minutesWorked;
quarterHours = minutesWorked/15;
return (quarterHours*RATE);
}
샘플 대화 상자
Welcome to the law office of
Dewey, Cheatham, and Howe.
The law office with a heart.
Enter the hours and minutes of your consultation:
5 46
For 5 hours and 46 minutes, your bill is $3450.00
call-by-value 매개 변수는 함수 본문 내에서 선언하는 변수와 마찬가지로 로컬 변수입니다.
그러나 형식 매개 변수에 대해 변수 선언을 추가해서는 안 됩니다.
예시)
아래의 경우처럼 minuitesWorked를 두 번 선언해서는 안 됩니다.
double fee( int hoursWorked, int minutesWorked)
{
int quarterHours;
int minutesWorked; // minutesWorked가 매개 변수인 경우에는 이 작업을 수행하지 마십시오!
Call-by-Reference 매개변수에 관한 연구
형식적인 매개변수를 call-by-reference 매개변수로 만들려면, 그것의 유형 이름에 앰퍼샌드 기호, &를 붙입니다.
함수를 호출할 때 대응하는 인수는 상수나 다른 표현식이 아니라 변수여야 합니다.
함수를 호출하면 (그 값이 아니라) 대응하는 변수 인수가 형식 매개변수로 대체됩니다.
함수 본문에서 형식 매개변수에 대한 변경은 함수를 호출할 때 변수로 변경됩니다.
치환 메커니즘에 대한 정확한 세부 사항은 이 장의 본문에 나와 있습니다.
EXAMPLE
void getData(int & firstInput, double & secondInput);
디스플레이 4.2는 참조 단위의 매개변수를 보여줍니다.
프로그램은 두 개의 숫자로 읽고 같은 숫자를 쓰지만 역순으로 적습니다.
함수 getNumbers 및 swapValues의 매개변수는 참조 단위의 매개변수입니다.
입력은 함수 호출에 의해 수행됩니다.
getNumbers(firstNum, secondNum);
변수 firstNum과 secondNum의 값은 이 함수 호출에 의해 설정됩니다.
그 후 다음 함수 호출은 두 변수 firstNum과 secondNum의 값을 반전시킵니다:
swapValues(firstNum, secondNum);
디스플레이 4.2 call-by-reference 매개변수
//Program to demonstrate call-by-reference parameters .
#include <iostream>
using namespacestd;
void getNumbers(int& input1, int& input2);
//Reads two integers from the keyboard .
void swapValues(int& variable1, int& variable2);
//Interchanges the values of variable1 and variable2.
void showResults(int output1, int output2);
//Shows the values of output1 and output2, in that order .
int main( )
{
int firstNum, secondNum;
getNumbers(firstNum, secondNum);
swapValues(firstNum, secondNum);
showResults(firstNum, secondNum);
return 0;
}
void getNumbers(int& input1, int& input2)
{
cout << "Enter two integers: ";
cin >> input1
>> input2;
}
void swapValues(int& variable1, int& variable2)
{
int temp;
temp = variable1;
variable1 = variable2;
variable2 = temp;
}
void showResults(int output1, int output2)
{
cout << "In reverse order the numbers are: "
<< output1 << " " << output2 << endl;
}
샘플 대화 상자
Enter two integers: 5 6
In reverse order the numbers are: 6 5
Call-by-Reference 메커니즘에 대한 세부 사항
call-by-reference 메커니즘은 함수 인수로 주어진 변수의 이름이 call-by-reference 형식 매개 변수로 문자 그대로 대체된 것처럼 작동합니다. 그러나 실제 과정은 주어진 변수들은 숫자로 된 고유한 주소(address)인 메모리의 위치로 구현됩니다.
예를 들어, 디스플레이 4.2의 프로그램이 컴파일될 때 변수 firstNum에는 위치 1010이 할당되고 변수 secondNum에는 1012가 할당될 수 있습니다. 모든 실용적인 목적을 위해 이 메모리 위치들이 변수입니다.
예를 들어, 디스플레이 4.2의 다음 함수 선언을 생각해 봅니다:
void getNumbers(int&input1, int&input2);
참조 단위 형식 매개변수 input1과 input2는 함수 호출에 사용되는 실제 인수의 자리 표시자입니다.
이제 동일한 프로그램에서 다음과 같은 함수 호출을 생각해 봅니다:
getNumbers(firstNum, secondNum);
함수 호출이 실행될 때 함수에는 인수 이름과 연관된 메모리 위치의 목록이 주어집니다. 이 예에서 목록은 위치로 구성됩니다
제1 메모리 위치는 제1 공식 파라미터와 연관되고, 제2 메모리 위치는 제2 공식 파라미터와 연관됩니다.
도식적으로, 이 경우 대응은
firstNum -> 1010 -> input 1
secondNum -> 1012 -> input 2
함수 문들이 실행될 때, 함수 본문이 공식 매개 변수에 대해 하는 모든 것은 실제로 그 공식 매개 변수와 연관된 메모리 위치의 변수에 수행됩니다.
상수 참조 매개변수
call-by-reference 매개변수의 타입 앞에 const를 놓으면 변경할 수 없는 call-by-reference 매개변수를 얻을 수 있습니다.
배열 및 클래스 유형의 파라미터를 사용하여 효율성을 높이는 데 도움이 될 것입니다.
예제: swapValues 함수
Display 4.2에 정의된 함수 swapValues는 두 변수에 저장된 값을 교환합니다.
void swapValue(int&variable1, int&variable2);
//변수1과 변수2의 값을 바꿉니다.
Display 4.2와 같이 swapValues 함수의 정의는 temp라는 로컬 변수를 사용합니다. 이 로컬 변수가 필요합니다.
함수 정의를 다음과 같이 단순화할 수 있다고 생각할 수 있습니다:
void swapValues( int& variable1, int& variable2)
{
variable1 = variable2;
variable2 = variable1;
}
이 대체 정의가 작동할 수 없는지 확인하려면 이 정의와 함수 호출에서 어떤 결과가 발생할지 고려해 보십시오.
swapValues(firstNum, secondNum);
변수 firstNum과 secondNum은 형식 매개 변수 variable1과 variable2로 대체되어 이 잘못된 함수 정의로 함수 호출은 다음과 같습니다:
firstNum = secondNum;
secondNum = firstNum;
이 코드는 원하는 결과를 생성하지 않습니다. firstNum의 값은 secondNum의 값과 동일하게 설정됩니다. 그러나 secondNum의 값은 변경된 firstNum의 값과 동일하게 설정됩니다. 변경된 firstNum의 값은 현재 secondNum의 원래 값입니다. 따라서 secondNum의 값은 전혀 변경되지 않습니다. (만약 이것이 불분명하다면, firstNum과 secondNum 변수에 대한 특정 값을 가진 단계를 거칩니다.)
ex) swapValues(2, 3); variable1 = 3; variable2 = 3; -> 두번째 값은 전혀 변경되지 않았다.
함수가 해야 할 일은 firstNum의 원래 값을 저장하여 값이 손실되지 않도록 하는 것입니다. 이는 올바른 함수 정의의 지역 변수 temp가 사용되는 용도입니다. 이 올바른 정의는 디스플레이 4.2의 정의입니다. 이 올바른 버전이 사용되고 함수가 firstNum과 secondNum을 인수로 호출되면 함수 호출은 다음 코드와 동일하며 올바르게 작동합니다:
temp = firstNum;
firstNum = secondNum;
secondNum = temp;
팁: 코드가 아닌 실행으로 생각해 보십시오
함수 호출이 어떻게 작동하는지를 함수 호출을 코드로 대체한다는 측면에서 설명할 수는 있지만 일반적으로 함수 호출에 대해 생각해야 하는 방식은 아닙니다. 대신 함수 호출을 실행으로 생각해야 합니다. 예를 들어, Display 4.2의 함수 swapValues와 다음과 같은 호출을 고려합니다.
swapValues(firstNum, secondNum);
함수 호출을 두 인수의 값을 교환하는 실행으로 생각하는 것이 더 쉽고 명확합니다. 코드로 생각하는 것이 훨씬 덜 명확합니다.
temp = firstNum;
firstNum = secondNum;
secondNum = temp;
혼합 매개변수 나열
같은 함수에서 call-by-value와 call-by-reference 형식 매개변수를 혼합하는 것은 완벽하게 합법적입니다.
예시)
void goodStuff( int& par1, int par2, double& par3);
매개변수와 인수
1. 함수에 대한 형식 매개 변수는 함수 선언에 나열되어 있으며 함수 정의 본문에서 사용됩니다.
형식 매개 변수는 함수를 호출할 때 무엇인가로 채워지는 일종의 공백 또는 자리 표시자입니다.
2. 인수는 형식 매개 변수를 채우기 위해 사용되는 것입니다.
함수 호출을 적을 때 인수는 함수 이름 뒤에 괄호 안에 나열됩니다.
함수 호출이 실행되면 인수는 형식 매개 변수에 대해 연결됩니다.
3. call-by-value 및 call-by-reference라는 용어는 플러그인 프로세스에서 사용되는 메커니즘을 말합니다.
call-by-value 방법에서는 인수의 값만 사용됩니다. 이 call-by-value 메커니즘에서 형식 매개변수는 해당 인수의 값으로 초기화되는 지역 변수입니다.
call-by-reference 메커니즘에서 인수는 변수이고 전체 변수가 사용됩니다. call-by-reference 메커니즘에서 인수 변수는 형식 매개변수를 대체하여 형식 매개변수에 대한 변경이 실제로 이루어지는 것입니다.
팁: 사용할 매개변수의 종류
디스플레이 4.3에서는 call-by-value 매개변수와 call-by-reference 매개변수의 차이를 보여준다.
par1Value는 call-by-value 매개변수이므로 지역 변수이다.
doStuff 함수 본문은 par1Value가 111로 설정되었고 이 값이 출력된다.
하지만 인수n1의 값은 변경되지 않고 1의 값을 유지한다.
par2Value는 call-by-reference 매개변수이다.
함수가 호출되면 변수 인수 n2(그 값뿐만 아니라)가 형식 파라미터 par2Ref에 대입된다.
doStuff 함수 본문은 par2Value가 222로 설정되었고 이 값이 출력된다.
함수 본체가 실행되면 변수 n2의 값이 변경되므로, 함수 호출에 의해 n2의 값이 2에서 222로 변경된다.
디스플레이 4.3 인수 메커니즘 비교하기
//Illustrates the difference between a call-by-value
//parameter and a call-by-reference parameter .
#include <iostream>
using namespacestd;
void doStuff(int par1Value, int& par2Ref);
//par1Value is a call-by-value formal parameter and
//par2Ref is a call-by-reference formal parameter .
int main( )
{
int n1, n2;
n1 = 1;
n2 = 2;
doStuff(n1, n2);
cout << "n1 after function call = " << n1 << endl;
cout << "n2 after function call = " << n2 << endl;
return 0;
}
void doStuff(int par1Value, int& par2Ref)
{
par1Value = 111;
cout << "par1Value in function call = "
<< par1Value << endl;
par2Ref = 222;
cout << "par2Ref in function call = "
<< par2Ref << endl;
}
샘플 대화 기록
par1Value in function call = 111
par2Ref in function call = 222
n1 after function call = 1
n2 after function call = 222
함정: 부주의한 지역 변수
함수가 변수의 값을 변경하려면 해당 형식 매개 변수가 call-by-reference 매개 변수여야 한다.
call-by-reference 매개 변수에서 앰퍼샌드(ampersand, &)를 부주의하게 생략하면 call-by-value 매개변수가 된다.
그러면 함수가 변수의 값을 변경할 수 없게 된다.
이것은 코드가 옳아 보이기 때문에 발견하기가 매우 어려울 수 있는 오류이다.
디스플레이 4.4의 경우 앰퍼샌드(&)가 함수 swapValues에서 잘못 누락되었다는 점을 제외하고는 Display 4.2의 프로그램과 유사하다.
하지만 swapValues가 함수 내부의 값만 변경할 뿐 firstNum, secondNum의 값을 변경하지 못한다.
하지만 올바른 프로그램과 거의 동일하게 보이고 오류 메시지 없이 컴파일되고 실행된다.
디스플레이 4.4 부주의한 지역 변수
//Program to demonstrate call-by-reference parameters .
#include <iostream>
using namespacestd;
void getNumbers(int& input1, int& input2);
//Reads two integers from the keyboard .
void swapValues(int variable1, int variable2); //Forgot the & here
//Interchanges the values of variable1 and variable2 .
void showResults(int output1, int output2);
//Shows the values of variable1 and variable2, in that order .
int main( )
{
int firstNum, secondNum;
getNumbers(firstNum, secondNum);
swapValues(firstNum, secondNum);
showResults(firstNum, secondNum);
return 0;
}
void swapValues(int variable1, int variable2) //Forgot the & here
{
int temp;
temp = variable1;
variable1 = variable2;
variable2 = temp;
//Inadvertent local variables(variable1, variable2)
}
//The definitions of getNumbers and
//showResults are the same as in Display 4.2 .
샘플 대화 기록
Enter two integers: 5 6
In reverse order the numbers are: 5 6
부주의한 지역 변수로 인한 에러가 발생했다.
팁: 형식 매개변수의 이름 선택하기
혼자 프로그래밍하는 것이 아니고 여러 프로그래머들이 함께 프로그래밍하고 쉽게 해석할 줄 알아야 하므로
형식 매개변수의 이름, 형식 매개변수를 대체할 인수을 선택할 때에는 가장 의미 있는 이름을 선택해야 합니다.
예: 피자 구입하기
피자에서 상품의 경제성은 (피자의 가격)/(피자의 양) = (피자의 가격)/(피자의 면적)=(피자의 가격)/{(피자의 반지름)의 제곱 * Pi} 로 판단한다.
디스플레이 4.5는 두 가지 크기의 피자 중에서 어떤 것이 더 잘 사는지를 결정하기 위해 소비자가 사용할 수 있는 프로그램을 보여준다.
디스플레이 4.5 피자 구입하기
//Determines which of two pizza sizes is the best buy .
#include <iostream>
using namespacestd;
void getData(int& smallDiameter, double& priceSmall,
int& largeDiameter, double& priceLarge);
void giveResults(int smallDiameter, double priceSmall,
int largeDiameter, double priceLarge);
double unitPrice(int diameter, double price);
//Returns the price per square inch of a pizza .
//Precondition: The diameter parameter is the diameter of the pizza
//in inches. The price parameter is the price of the pizza .
int main( )
{
int diameterSmall, diameterLarge;
double priceSmall, priceLarge;
getData(diameterSmall, priceSmall, diameterLarge, priceLarge);
giveResults(diameterSmall, priceSmall, diameterLarge, priceLarge);
//변수 diameter Small, diameter Large, price Small 및 price Large는
//함수 getData에서 함수 giveResults로 데이터를 운반하는 데 사용됩니다.
return 0;
}
void getData(int& smallDiameter, double& priceSmall,
int& largeDiameter, double& priceLarge)
{
cout << "Welcome to the Pizza Consumers Union.\n";
cout << "Enter diameter of a small pizza (in inches): ";
cin >> smallDiameter;
cout << "Enter the price of a small pizza: $";
cin >> priceSmall;
cout << "Enter diameter of a large pizza (in inches): ";
cin >> largeDiameter;
cout << "Enter the price of a large pizza: $";
cin >> priceLarge;
}
void giveResults(int smallDiameter, double priceSmall,
int largeDiameter, double priceLarge)
{
double unitPriceSmall, unitPriceLarge;
//하나의 함수가 다른 함수 내에서 호출됨
unitPriceSmall = unitPrice(smallDiameter, priceSmall);
unitPriceLarge = unitPrice(largeDiameter, priceLarge);
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
cout << "Small pizza:\n"
<< "Diameter = " << smallDiameter << " inches\n"
<< "Price = $" << priceSmall
<< " Per square inch = $" << unitPriceSmall << endl
<< "Large pizza:\n"
<< "Diameter = " << largeDiameter << " inches\n"
<< "Price = $" << priceLarge
<< " Per square inch = $" << unitPriceLarge << endl;
if (unitPriceLarge < unitPriceSmall)
cout << "The large one is the better buy.\n";
else
cout << "The small one is the better buy.\n";
cout << "Buon Appetito!\n";
}
double unitPrice(int diameter, double price)
{
const doublePI = 3.14159;
double radius, area;
radius = diameter/ static_cast<double>(2);
area = PI * radius * radius;
return (price/area);
}
샘플 대화 상자
Welcome to the Pizza Consumers Union.
Enter diameter of a small pizza (in inches): 10
Enter the price of a small pizza: $7.50
Enter diameter of a large pizza (in inches): 13
Enter the price of a large pizza: $14.75
Small pizza: Diameter = 10 inches
Price = $7.50 Per square inch = $0.10
Large pizza: Diameter = 13 inches
Price = $14.75 Per square inch = $0.11
The small one is the better buy.
Buon Appetito!
4.2 오버로딩 및 기본 인수
오버로딩 소개
오버로딩(overloading): 동일한 함수 이름에 대해 두 개 이상의 함수 정의를 부여하는 것
함수 이름을 오버로드할 때 함수 정의에는 형식 매개 변수의 개수가 달라야 하거나 형식이 다른 일부 형식 매개 변수가 있어야 합니다.
함수 호출이 있을 때 컴파일러는 함수 호출의 인수와 일치하는 형식 매개 변수의 수와 형식 매개 변수의 유형이 포함된 함수 정의를 사용합니다.
서명
함수의 서명: const 키워드를 포함하지 않고 앰퍼샌드(ampersand, &)를 포함하지 않는 매개 변수 목록의 유형 순서를 가진 함수 이름
함수 이름을 오버로드할 때 함수 이름의 두 정의는 이 서명의 정의를 사용하여 서로 다른 서명을 가져야 합니다.
(일부 기관에서는 서명의 일부로 const 및/또는 앰퍼샌드(ampersand, &)를 포함하지만, 우리는 오버로드를 설명하는 데 사용되는 정의를 원합니다.)
디스플레이 4.6 함수 이름 오버로딩하기
//Illustrates overloading the function name ave .
#include <iostream>
using namespacestd;
double ave(double n1, double n2);
//Returns the average of the two numbers n1 and n2 .
double ave(double n1, double n2, double n3);
//Returns the average of the three numbers n1, n2, and n3 .
int main( )
{
cout << "The average of 2.0, 2.5, and 3.0 is "
<< ave(2.0, 2.5, 3.0) << endl;
cout << "The average of 4.5 and 5.5 is "
<< ave(4.5, 5.5) << endl;
return 0;
}
double ave(double n1, double n2) //Two arguments
{
return ((n1 + n2)/2.0);
}
double ave(double n1, double n2, double n3) //Three arguments
{
return ((n1 + n2 + n3)/3.0);
}
샘플 대화 상자
The average of 2.0, 2.5, and 3.0 is 2.5
The average of 4.5 and 5.5 is 5.0
함정: 자동 형변환 및 오버로딩
프로그램에서 다음 함수 정의가 발생하고 함수 이름 mpg를 오버로드하지 않았다고 가정합니다(따라서 이것이 mpg라고 하는 함수의 유일한 정의입니다).
double mpg(double miles, double gallons)
//갤런당 마일을 반환합니다.
{
return (miles / gallons);
}
만약 함수 mpg를 int형 인수로 호출하면 C++는 자동으로 int형 인수를 double형 값으로 변환합니다. 따라서 다음은 화면에 갤런당 22.5 마일을 출력합니다:
cout << mpg(45, 2) << " miles per gallon";
C++는 45를 45.0으로, 2를 2.0으로 변환한 후 분할 45.0/2.0을 수행하여 반환된 값인 22.5를 얻습니다.
함수가 double형의 인수를 요구하고 당신이 그것에 int형의 인수를 준다면, C++는 자동적으로 int형의 인수를 double형의 값으로 변환할 것입니다. 이것은 매우 유용하고 자연스러워서 우리는 거의 생각하지 못합니다.
그러나 오버로딩은 자동 형변환을 방해할 수 있습니다. 예를 들어 보겠습니다.
함수 이름 mpg를 (불행하게) 오버로드하여 프로그램이 이전의 정의와 마찬가지로 다음과 같은 mpg 정의를 포함하도록 했다고 가정합니다:
int mpg( int goals, int misses)
//완벽한 목표의 척도를 반환합니다
//((goals - misses)로 계산됩니다.
{
return (goals — misses);
}
함수 이름 mpg에 대한 이 두 정의가 모두 포함된 프로그램에서 다음은 (불행하게도 43은 45 – 2이므로) 갤런당 43마일을 출력합니다:
cout << mpg(45, 2) << " miles per gallon";
C++가 두 개의 형식 매개변수가 int인 mpg(45, 2)라는 함수를 볼 때, C++는 먼저 두 형식 매개변수의 타입이 int인 mpg의 함수 정의를 찾습니다. 만약 그러한 함수 정의를 찾는다면, C++는 그 함수 정의를 사용합니다. 그것이 일치하는 함수 정의를 찾을 수 있는 유일한 방법이 아니라면, C++는 int형의 인수를 double형의 값으로 변환하지 않습니다.
mpg 예제는 오버로드에 대한 한 가지 더 설명합니다. 두 개의 관련 없는 함수에 동일한 함수 이름을 사용해서는 안 됩니다. 이러한 부주의한 함수 이름 사용은 결국 혼란을 야기할 것이 확실합니다.
오버로딩 해결 규칙
컴파일러가 주어진 함수 호출에 적용할 함수 이름의 여러 오버로딩 정의 중 어느 것을 해결하는 데 사용하는 규칙은 다음과 같습니다:
1. 정확한 일치: 인수의 수와 유형이 (자동 유형 변환 없이) 정의와 정확히 일치할 경우 사용되는 정의입니다.
2. 정수형 또는 부동 소수점형(예: short to int 또는 float to double)과 같은 프로모션을 사용하는 매치. (bool에서 int로의 변환 및 char에서 int로의 변환은 정수형 내 프로모션으로 간주됩니다.)
3. int에서 double로와 같이 미리 정의된 유형의 다른 변환을 사용하여 일치합니다.
4. 사용자 정의 유형의 변환을 사용하여 일치합니다(8장 참조).
5. 타원을 사용한 매치... (이 책에서는 다루지 않으며, 사용하지 않을 경우에는 문제가 되지 않습니다.)
처음에 일치하는 것이 발견되는 단계에서 두 개의 일치하는 것이 발견되면 애매한 상황이 발생하여 오류 메시지가 표시됩니다.
예: 개정된 피자 구매 프로그램
피자소비자연합은 우리가 디스플레이 4.5에 작성한 프로그램으로 매우 성공적이었습니다.
그러나 업주들은 자신들의 비열한 행동을 계속하기를 원하며 소비자들을 속일 수 있는 새로운 방법을 생각해 냈습니다. 그들은 이제 둥근 피자와 직사각형 피자를 모두 제공합니다. 그들은 우리가 쓴 프로그램이 직사각형 모양을 다룰 수 없다는 것을 알고 있습니다.
디스플레이 4.7은 둥근 피자와 직사각형 피자를 비교하는 저희 프로그램의 또 다른 버전입니다. 주의할 점은 price라는 함수명이 오버로드되어서 둥근 피자와 직사각형 피자 모두에 적용이 된다는 것입니다.
디스플레이 4.7 개정된 피자 구매 프로그램
//Determines whether a round pizza or a rectangular pizza is the best
//buy.
#include <iostream>
using namespace std;
double unitPrice(int diameter, double price);
//Returns the price per square inch of a round pizza .
//The formal parameter named diameter is the diameter of the pizza
//in inches. The formal parameter named price is the price of the pizza .
double unitPrice(int length, int width, double price);
//Returns the price per square inch of a rectangular pizza
//with dimensions length by width inches .
//The formal parameter price is the price of the pizza .
int main( )
{
int diameter, length, width;
double priceRound, unitPriceRound,
priceRectangular, unitPriceRectangular;
cout << "Welcome to the Pizza Consumers Union.\n";
cout << "Enter the diameter in inches"
<< " of a round pizza: ";
cin >> diameter;
cout << "Enter the price of a round pizza: $";
cin >> priceRound;
cout << "Enter length and width in inches\n"
<< "of a rectangular pizza: ";
cin >> length >> width;
cout << "Enter the price of a rectangular pizza: $";
cin >> priceRectangular;
unitPriceRectangular =
unitPrice(length, width, priceRectangular);
unitPriceRound = unitPrice(diameter, priceRound);
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
cout << endl
<< "Round pizza: Diameter = "
<< diameter << " inches\n"
<< "Price = $" << priceRound
<< " Per square inch = $" << unitPriceRound
<< endl
<< "Rectangular pizza: Length = "
<< length << " inches\n"
<< "Rectangular pizza: Width = "
<< width << " inches\n"
<< "Price = $" << priceRectangular
<< " Per square inch = $" << unitPriceRectangular
<< endl;
if (unitPriceRound < unitPriceRectangular)
cout << "The round one is the better buy.\n";
else
cout << "The rectangular one is the better buy.\n";
cout << "Buon Appetito!\n";
return 0;
}
double unitPrice(int diameter, double price)
{
const doublePI = 3.14159;
double radius, area;
radius = diameter/double(2);
area = PI * radius * radius;
return (price/area);
}
double unitPrice(int length, int width, double price)
{
double area = length * width;
return (price/area);
}
샘플 대화 상자
Welcome to the Pizza Consumers Union.
Enter the diameter in inches of a round pizza: 10
Enter the price of a round pizza: $8.50
Enter length and width in inches of a rectangular pizza: 6 4
Enter the price of a rectangular pizza: $7.55
Round pizza: Diameter = 10 inches
Price = $8.50 Per square inch = $0.11
Rectangular pizza: Length = 6 inches
Rectangular pizza: Width = 4 inches
Price = $7.55 Per square inch = $0.31
The round one is the better buy.
Buon Appetito!
기본 인수
기본 인수(default argument): 함수나 메서드에 해당 인수가 전달되지 않으면 자동으로 사용되는 값
함수에서 하나 이상의 값별 호출 매개 변수에 기본 인수를 지정할 수 있습니다. 해당 인수가 생략되면 기본 인수로 대체됩니다.
기본 인수는 함수가 처음 선언될 때(또는 정의된 경우, 먼저 정의된 경우) 주어집니다. 이후의 선언이나 다음 정의는 주어진 인수가 이전에 주어진 인수와 일치하더라도 일부 컴파일러는 이를 오류로 간주하기 때문에 기본 인수를 다시 주어서는 안 됩니다.
두 개 이상의 기본 인수를 가질 수 있지만 모든 기본 인수 위치는 가장 오른쪽에 있어야 합니다.
두 개 이상의 기본 인수가 있는 경우 함수가 호출되면 오른쪽에서 시작하는 인수를 생략해야 합니다.
기본 인수는 제한된 가치가 있지만 때때로 인수에 대한 사용자의 사고 방식을 반영하는 데 사용될 수 있습니다.
기본 인수는 call-by-value 매개 변수에만 사용할 수 있습니다. call-by-reference 매개 변수의 경우에는 사용할 수 없습니다.
기본 인수 버전은 오버로딩 버전보다 짧을 수 있지만 기본 인수 버전으로 수행할 수 있는 작업은 오버로딩을 사용하여 수행할 수 있습니다.
디스플레이 4.8 기본 인수
#include <iostream>
using namespace std;
void showVolume(int length, int width = 1, int height = 1); //Default Arguments
//Returns the volume of a box .
//If no height is given, the height is assumed to be 1 .
//If neither height nor width is given, both are assumed to be 1 .
int main( )
{
showVolume(4, 6, 2);
showVolume(4, 6);
showVolume(4);
return 0;
}
//A default argument should not be given a second time.
void showVolume(int length, int width, int height)
{
cout << "Volume of a box with \n"
<< "Length = " << length << ", Width = " << width << endl
<< "and Height = " << height
<< " is " << length*width*height << endl;
}
샘플 대화 상자
Volume of a box with
Length = 4, Width = 6
and Height = 2 is 48
Volume of a box with
Length = 4, Width = 6
and Height = 1 is 24
Volume of a box with
Length = 4, Width = 1
and Height = 1 is 4
4.3 테스팅 및 디버깅 기능
assert 매크로
주장(assertion): 참이거나 거짓인 문장. 프로그램의 정확성을 문서화하고 확인하는 데 사용된다. 예)전제조건, 사후조건
asset 매크로(macro)의 특징
assertion을 부울식으로 바꾸면, assert 매크로가 코드가 assertion을 만족하는지 여부를 확인할 수 있다.
asset 매크로는 bool타입의 call-by-value 매개변수를 가지는 void 함수처럼 사용된다.
인수가 true로 평가되면 아무 일도 일어나지 않고, 인수가 false로 평가되면 프로그램이 종료되고 에러 메세지를 출력한다.
예를 들어, 다음 함수 선언은 프로그래밍 프로젝트 4.3에서 가져온 것입니다:
void computeCoin (int coinValue, int&number, int&mountLeft);
//Precondition: 0 < coinValue < 100; 0 <= amountLeft < 100.
//Postcondition: number has been set equal to the maximum number
//of coins of denomination coinValue cents that can be obtained
//from amountLeft cents. amountLeft has been decreased by the
//value of the coins, that is, decreased by number*coinValue .
다음 예제와 같이 기능 호출에 대한 전제 조건이 유지되는지 확인할 수 있습니다:
assert((0 < currentCoin) && (currentCoin < 100)
&& (0 <= currentAmountLeft) && (currentAmountLeft < 100));
computeCoin(currentCoin, number, currentAmountLeft);
전제 조건이 충족되지 않으면 프로그램이 종료되고 오류 메시지가 출력됩니다.
assert 매크로는 라이브러리 cassert에 정의되므로 assert 매크로를 사용하는 모든 프로그램은 다음을 포함해야 합니다:
#include <cassert>
assert를 사용하는 한 가지 장점은 assert 호출을 끌 수 있다는 것입니다. 프로그램에서 assert 호출을 사용하여 프로그램을 디버깅한 다음 사용자가 이해할 수 없는 오류 메시지를 받지 않도록 해제할 수 있습니다.
이렇게 하면 프로그램에서 수행되는 오버헤드가 줄어듭니다.
프로그램에서 모든 #define NDEBUG assertion들을 끄기 위해, include 지시문 전에 #define NDEBUG를 추가합니다:
#define NDEBUG
#include <cassert>
따라서 프로그램이 완전히 디버그된 후 프로그램에 #define NDEBUG를 삽입하면 프로그램의 모든 assertion 호출이 해제됩니다.
나중에 프로그램을 변경하여 다시 디버그해야 하는 경우 #define NDEBUG 줄을 삭제하거나 주석을 추가하여 assertion 호출을 다시 켤 수 있습니다.
스텁 및 드라이버
드라이버 프로그램(driver probram): 함수를 테스트 수행하기 위한 외부의 프로그램
예시) 디스플레이 4.5의 unitPrice 함수를 테스트하기 위한 디스플레이 4.9
스텁(stub): 아직 작성되지 않았거나 테스트되지 않은 함수의 단순화된 버전의 함수로 테스트. 반드시 정확한 계산을 수행하지는 않지만 테스트에 충분한 값을 제공하며, 성능에 대한 신뢰를 가질 수 있을 만큼 충분히 간단하다.
예시)
//A stub. The final function definition must still be written .
double unitPrice( int diameter, double price)
{
return (9.99);//Not correct but good enough for a stub.
}
스터브와 함께 프로그램 개요를 사용하면 각 함수를 테스트하기 위해 완전히 새로운 프로그램을 작성하는 대신 기본 프로그램 개요를 테스트하고 구체화할 수 있습니다.
이러한 이유로, 스터브가 있는 프로그램 개요는 대개 가장 효율적인 테스트 방법입니다. 일반적인 접근법은 드라이버 프로그램을 사용하여 입력과 출력과 같은 일부 기본 기능을 테스트하고 나머지 기능을 스터브가 있는 프로그램을 사용하여 테스트하는 것입니다. 스터브는 한 번에 하나의 함수로 대체됩니다. 하나의 스터브는 완전한 함수로 대체되어 테스트되고, 그 기능이 완전히 테스트되면 다른 스터브는 완전한 함수 정의로 대체되어 최종 프로그램이 생성될 때까지 계속됩니다.
함수 테스트의 기본규칙
모든 함수는 해당 프로그램의 다른 모든 함수가 이미 완전히 테스트되고 디버깅된 프로그램에서 테스트되어야 합니다.
디스플레이 4.9 드라이버 프로그램
//Driver program for the function unitPrice .
#include <iostream>
using namespacestd;
double unitPrice(int diameter, double price);
//Returns the price per square inch of a pizza .
//Precondition: The diameter parameter is the diameter of the pizza
//in inches. The price parameter is the price of the pizza .
int main( )
{
double diameter, price;
char ans;
do
{
cout << "Enter diameter and price:\n";
cin >> diameter >> price;
cout << "unit Price is $"
<< unitPrice(diameter, price) << endl;
cout << "Test again? (y/n)";
cin >> ans;
cout << endl;
} while (ans == 'y' || ans == 'Y');
return 0;
}
double unitPrice(int diameter, double price)
{
const doublePI = 3.14159;
double radius, area;
radius = diameter/ static_cast<double>(2);
area = PI * radius * radius;
return (price/area);
}
샘플 대화 상자
Enter diameter and price:
13 14.75
Unit price is: $0.111126
Test again? (y/n): y
Enter diameter and price:
2 3.15
Unit price is: $1.00268
Test again? (y/n): n