[따배C++] 7. 함수
Created: June 6, 2021 11:26 AM
Tag: assert, call by reference, command line arg., ellipsis, function pointer, inline, overloading, recursion, tuple, vector to stack
7.3 참조에 의한 인수 전달 call by reference
call by reference를 통한 인수 전달은 변수 자체를 전달하는 개념으로, 복사가 진행되지 않기 때문에 문법이 간결해지고 퍼포먼스 부하가 없다. call by pointer도 결국 주소라는 값을 call by value 처리하는것이기 때문에 주소값이 복사되며 추가적으로 포인터 변수가 선언되기 때문에(포인터도 결국 변수이다) 메모리 비효율이 발생한다.
#include <iostream>
using namespace std;
void addOne(int &y)
{
y = y + 1;
}
int main(void)
{
int x = 5;
cout << x << " " << &x << endl;
addOne(x);
cout << x << " " << &x << endl;
}
// result
5 0x7ffee24fb87c
6 0x7ffee24fb87c
#include <iostream>
#include <cmath> // sin(), cos()
using namespace std;
void getSinCos(const double °rees, double &sin_out, double &cos_out)
// 입력을 앞에 두고(const 를 붙여주고) 출력을 뒤로 배치하며 래퍼런스로 받는것이 트랜드!
{
static const double pi = 3.141592 / 180.0;
// 함수가 여러번 호출된다면 정적으로 선언하여 재활용하자
const double radians = degrees * pi;
// const: 한번 계산되고 더이상 변하지 않을 값은 const를 붙여 고정해주자
sin_out = std::sin(radians);
cos_out = std::cos(radians);
}
// 먼저 변수를 선언하고 call by reference로 값을 바꾸어
// 마치 반환값이 여러개인 것처럼 기능할 수 있다
int main(void)
{
double sin(0.0);
double cos(0.0);
getSinCos(30.0, sin, cos);
cout << sin << " " << cos << endl;
return (0);
}
array
#include <iostream>
#include <vector>
using namespace std;
void printElements(const vector<int> &arr)
{
// do something
}
int main(void)
{
vector<int> arr{ 1, 2, 3, 4 };
printElements(arr);
return (0);
}
7.5 다양한 반환 값들(값, 참조, 주소, 구조체, 튜플) tuple
return by address
#include <iostream>
using namespace std;
int *getValue(int x)
{
int value = x * 2;
return (&value);
}
int main(void)
{
// int *value = getValue(3);
int value = *getValue(3); // 사라질 변수를 디래퍼런싱 하는것이 때문에 위험하다
cout << value << endl;
return (0);
}
return by address & array
#include <iostream>
#include <array>
using namespace std;
int &get(std::array<int, 100> &my_array, int ix)
{
return (my_array[ix]);
}
int main(void)
{
std::array<int, 100> my_array;
my_array[30] = 10;
get(my_array, 30) *= 10; // 마치 변수인것 처럼 // 이런 패턴으로 사용하는 경우가 많다
cout << my_array[30] << endl;
return (0);
}
tuple → 함수의 리턴값을 여러개!
#include <iostream>
using namespace std;
int *getValue(int x)
{
int value = x * 2;
return (&value);
}
int main(void)
{
// int *value = getValue(3);
int value = *getValue(3); // 사라질 변수를 디래퍼런싱 하는것이 때문에 위험하다
cout << value << endl;
return (0);
}
#include <iostream>
#include <array>
using namespace std;
int &get(std::array<int, 100> &my_array, int ix)
{
return (my_array[ix]);
}
int main(void)
{
std::array<int, 100> my_array;
my_array[30] = 10;
get(my_array, 30) *= 10; // 마치 변수인것 처럼 // 이런 패턴으로 사용하는 경우가 많다
cout << my_array[30] << endl;
return (0);
}
함수의 리턴값을 여러개 받고자 하는 경우 구조체를 사용하는것이 일반적이었으나 함수마다 구조체를 선언해야 하는 번거로움이 존재한다. 이와같은 불편함을 해소하고자 현대에는 튜플을 사용한다.
#include <iostream>
#include <array>
#include <tuple>
using namespace std;
std::tuple<int, double> getTuple()
// std::tuple<int, double> 자체가 마치 사용자 정의 자료형인 것처럼 기능한다
{
int a = 10;
double d = 3.14;
return (std::make_tuple(a, d));
}
int main(void)
{
// std::tuple<int, double> my_tp = getTuple();
// cout << std::get<0>(my_tp) << endl; // a
// cout << std::get<1>(my_tp) << endl; // d
// C++ 17 (modern C++)
auto[a, d] = getTuple();
cout << a << endl;
cout << d << endl;
return (0);
}
7.6 인라인 함수
인라인 함수는 보통 헤더파일에 함수를 정의할때 사용한다. 인라인 함수는 호출을 하지 않으며 지역변수가 선언되는 과정을 생략하고 함수의 코드를 그 자리에서 그대로 실행한다. 즉, 컴파일러는 함수를 사용하는 부분에 함수의 코드를 복제해서 넣어준다. inline
코드를 삽입하는것은 컴파일러 입장에서 인라인 기능을 사용하는것을 강제하는것이 아닌 권유하는 것으로 해석되므로 모든 함수를 인라인으로 바꾼다고 효율성이 개선(최적화)되는 것은 아니다. 또한 현대에는 컴파일러 자체적으로 코드의 논리구조를 파악하여 인라인 기능을 활성화하는 추세이므로 인라인 함수에 의존하여 최적화를 기대하는것은 바람직하지 않다.
#include <iostream>
using namespace std;
inline int min(int x, int y)
{
return ((x > y) ? y : x);
}
int main(void)
{
cout << min(5, 6) << endl;
cout << min(3, 2) << endl;
cout << (5 > 6 ? 6 : 5) << endl;
cout << (3 > 2 ? 2 : 3) << endl; // 컴파일러 레벨에서 이처럼 치환되어 작동한다
}
7.7 함수 오버로딩 function overloading
함수 오버로딩을 사용하여 동일한 이름을 갖는 함수를 사용(재정의)할 수 있다. 함수의 정체성은 함수의 이름 뿐만이 아닌 파라미터에 의해서도 결정된다. 따라서 동일한 이름을 갖고 파리미터의 성질이 다른 두가지 함수를 선언하고 적절히 사용하게 되면 컴파일러가 함수를 자동으로 매치하여 작동한다. 하지만 이 경우 역시 모호성(ambiguity) 문제가 발생한다. 이때 적절한 casting을 통해 자료형을 명확히 함으로서 모호성을 해소할 수 있다.
int add(int x, int y)
{
return (x + y);
}
double add(double x, double y)
{
return (x + y);
}
int main(void)
{
add(1, 2);
add(3.0, 4.0);
}
#include <iostream>
void getRandom(int &x) {}
void getRandom(double &x) {}
void print(unsigned int value) {}
void print(float value) {}
int main(void)
{
int x = getRandom(x);
int x = getRandom(double());
print(0u);
print(3.14159f);
return (0);
}
1) 오버로딩(Overloading)
- 메서드의 이름은 같고 매개변수의 갯수나 타입이 다른 함수를 정의하는 것을 의미한다.
- 리턴값만을 다르게 갖는 오버로딩은 작성 할 수 없다.
2) 오버라이딩(Overriding)
- over + ride 상위 클래스의 메서드를 하위 클래스가 재정의 하는 것이다.메서드의 이름은 물론 파라메터의 갯수나 타입도 동일해야 하며, 주로 상위 클래스의 동작을 상속받은 하위 클래스에서 변경하기 위해 사용된다.즉,오버로딩(Overloading)은 기존에 없던 새로운 메서드를 정의하는 것이고,오버라이딩(Overriding)은 상속 받은 메서드의 내용만 다소 변경 하는 것이다.
7.8 매개변수의 기본값 default parameters
매개변수의 기본값을 설정하는 경우 헤더에 프로토타입이 위치하는 경우 중복으로 기본값을 성정하지 않도록 주의해야 한다. 이는 함수 오버로딩에도 활용할 수 있다
#include <iostream>
using namespace std;
void print(int x = 10, int y = 20, int z = 30)
{
cout << x << " " << y << " " << z << endl;
}
int main(void)
{
print();
print(100);
print(100, 200);
print(100, 200, 300);
return (0);
}
// result
10 20 30
100 20 30
100 200 30
100 200 300
7.9 함수 포인터
#include <iostream>
using namespace std;
int func()
{
return (5);
}
int goo()
{
return (10);
}
int main(void)
{
int(*fcnptr)() = func;
cout << fcnptr() << endl; // 5
fcnptr = goo;
cout << fcnptr() << endl; // 10
return (0);
}
#include <iostream>
using namespace std;
int func()
{
return (5);
}
int goo()
{
return (10);
}
int main(void)
{
int(*fcnptr)() = func;
cout << fcnptr() << endl; // 5
fcnptr = goo;
cout << fcnptr() << endl; // 10
return (0);
}
typedef
, using
, functional
세가지 방법을 통해 함수를 매개변수로 좀더 편리하게 사용할 수 있다.
#include <iostream>
#include <array>
using namespace std;
bool isEven(const int &number)
{
if (number % 2 == 0) return (true);
else return (false);
}
bool isOdd(const int &number)
{
if (number % 2 != 0) return (true);
else return (false);
}
// 1. typedef
typedef bool (*check_fcn_t)(const int &);
// 2. type alias
using check_fcn_t = bool(*)(const int &);
// 3. functional (C++ 11)
#include <functional>
// 1. 2.
void printNumbers(const array<int, 10> &my_array, check_fcn_t check_fcn)
// 3.
void printNumbers(const array<int, 10> &my_array, std::function<bool(const int &)> check_fcn)
{
for (auto element : my_array) // for each
{
if (check_fcn(element) == true) cout << element;
}
cout << endl;
}
int main(void)
{
std::array<int, 10> my_array{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
// 1. 2.
printNumbers(my_array, isEven);
printNumbers(my_array, isOdd);
// 3.
std::function<bool(const int &)> fcnptr = isEven;
printNumbers(my_array, fcnptr);
fcnptr = isOdd;
printNumbers(my_array, isOdd);
return (0);
}
7.10 스택과 힙 the stack and the heap
스택
스택 오버플로우
동적할당
힙의 자료구조는 스택이 아니기 때문에 메모리의 위치를 예측하기 힘들다.
7.11 std vector를 스택처럼 사용하기
백터에는 size와 capacity 두가지 개념이 존재한다. capacity는 실제 프로그램에 의해 할당되는 메모리량이다. size는 그 중에서 일부인 눈에 보이는 사용량을 의미한다. 동적할당과 해제의 경우 속도가 느리므로 퍼포먼스에 상당한 영향을 미친다. 스택 대신 std vector를 사용하고 reserve
를 활용하면 push_back
시 capacity를 늘릴 필요가 없으므로 유용하다.
#include <iostream>
#include <vector>
using namespace std;
void printStack(const std::vector<int> &v)
{
for (auto &e : v) // for each
cout << e << " ";
cout << endl;
}
int main(void)
{
std::vector<int> v { 1, 2, 3 };
std::vector<int> stack;
stack.reserve(1024);
stack.push_back(3);
printStack(stack);
stack.push_back(5);
printStack(stack);
stack.push_back(7);
printStack(stack);
stack.pop_back();
printStack(stack);
stack.pop_back();
printStack(stack);
stack.pop_back();
printStack(stack);
return (0);
}
// result
3
3 5
3 5 7
3 5
3
7.13 재귀적 함수 호출 recursive function call == recursion
재귀적 호출을 사용하는 경우 반드시 종료조건을 설정해주어야 하며 스택 오버플로우에 유의해야 한다. 가능한 반복문으로 처리하는것이 더 효율적이다. 재귀문을 활용하는 것이 매우 편리한 알고리즘이 아닌 경우 반복문을 통해 구현하는것이 좋다.
#include <iostream>
using namespace std;
void countDown(int count)
{
cout << count << endl;
if (count > 0)
countDown(count - 1);
}
int main(void)
{
countDown(5);
return (0);
}
7.14 단언하기 assert
#include <cassert> // assert.h
#include <array>
int main(void)
{
assert(false);
// 디버그 모드에서만 작동, 릴리즈 모드에서는 무시
// 런타임에서 발생하는 오류의 위치를 알려준다
// ...
assert(number == 5); // number should be 5
const int x = 5;
static_assert(x == 5, "x should be 5");
// 컴파일타임에서 발생하는 오류의 위치를 알려준다
return (0);
}
7.15 명령줄 인수 command line arguments
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
for (int count = 0; count < argc; ++count)
{
std::string argv_single = argv[count];
if (count == 1)
{
int input_number = std::stoi(argv_single);
cout << input_number + 1 << endl;
}
else
cout << argv_single << endl;
}
return (0);
}
7.16 생략부호 ellipsis
#include <iostream>
#include <cstarg> // for ellipsis
using namespace std;
double findAverage(int count, ...)
{
double sum = 0;
va_list list;
va_start(list, count); // 인자의 개수를 알려줘야
for (int arg = 0; arg < count; ++arg)
sum += va_arg(list, int);
va_end(list);
return (sum / count);
}
int main(void)
{
cout << findAverage(3, 1, 2, 3) << endl;
return (0);
}
Author And Source
이 문제에 관하여([따배C++] 7. 함수), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@24siefil/C-7
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
#include <cassert> // assert.h
#include <array>
int main(void)
{
assert(false);
// 디버그 모드에서만 작동, 릴리즈 모드에서는 무시
// 런타임에서 발생하는 오류의 위치를 알려준다
// ...
assert(number == 5); // number should be 5
const int x = 5;
static_assert(x == 5, "x should be 5");
// 컴파일타임에서 발생하는 오류의 위치를 알려준다
return (0);
}
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
for (int count = 0; count < argc; ++count)
{
std::string argv_single = argv[count];
if (count == 1)
{
int input_number = std::stoi(argv_single);
cout << input_number + 1 << endl;
}
else
cout << argv_single << endl;
}
return (0);
}
7.16 생략부호 ellipsis
#include <iostream>
#include <cstarg> // for ellipsis
using namespace std;
double findAverage(int count, ...)
{
double sum = 0;
va_list list;
va_start(list, count); // 인자의 개수를 알려줘야
for (int arg = 0; arg < count; ++arg)
sum += va_arg(list, int);
va_end(list);
return (sum / count);
}
int main(void)
{
cout << findAverage(3, 1, 2, 3) << endl;
return (0);
}
Author And Source
이 문제에 관하여([따배C++] 7. 함수), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@24siefil/C-7
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
#include <iostream>
#include <cstarg> // for ellipsis
using namespace std;
double findAverage(int count, ...)
{
double sum = 0;
va_list list;
va_start(list, count); // 인자의 개수를 알려줘야
for (int arg = 0; arg < count; ++arg)
sum += va_arg(list, int);
va_end(list);
return (sum / count);
}
int main(void)
{
cout << findAverage(3, 1, 2, 3) << endl;
return (0);
}
Author And Source
이 문제에 관하여([따배C++] 7. 함수), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@24siefil/C-7저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)