Show Lecture.Functions as a slide show.
By & large, C++ functions look like Java functions:
int sum(int a, int b) { return a+b; } int main() { cout << sum(2, 40) << '\n'; }
42
Functions can have default trailing arguments:
int sum(int a, int b = 1) { return a+b; } int main() { cout << sum(2, 40) << '\n' << sum(8) << '\n'; }
42 9
All default arguments must be piled up at the end:
int sum(int a = 123, int b = 456) { return a+b; } int main() { cout << sum(2, 40) << '\n' << sum(8) << '\n' << sum() << '\n'; }
42 464 579
Like Java, C++ can have multiple versions of the same function that take different arguments:
int sum(int a, int b) { return a+b; } int sum(int a) { return a+456; } int sum() { return 123+456; } int main() { cout << sum(2, 40) << '\n' << sum(8) << '\n' << sum() << '\n'; }
42 464 579
The types don’t have to be the same, either:
int sum(int a, int b) { return a+b; } double sum(int a) { return a+4.5; } string sum(double) { return "I can’t deal with double"; } const char *sum() { return "You are a bonehead"; } int main() { cout << sum(2, 40) << '\n' << sum(8) << '\n' << sum(123.456) << '\n' << sum() << '\n'; }
42 12.5 I can’t deal with double You are a bonehead
sum
in the executable file?
sum
is sum
, right?
sum
.
sum-that-takes-an-int-argument
.
int foo() { return 1; } double foo() { return 2.0; } int main() { return 0; }
c.cc:2: error: ambiguating new declaration of 'double foo()'
$ cat ~cs253/Example/overload.cc int sum(int a, int b) { return a+b; } double sum(int a) { return a+4.5; } const char *sum() { return "You are a bonehead"; } bool sum(double, int, short) { return false; } $ g++ -c ~cs253/Example/overload.cc $ nm --numeric-sort overload.o 0000000000000000 T _Z3sumii 0000000000000014 T _Z3sumi 000000000000002e T _Z3sumv 0000000000000039 T _Z3sumdis $ nm --numeric-sort --demangle overload.o 0000000000000000 T sum(int, int) 0000000000000014 T sum(int) 000000000000002e T sum() 0000000000000039 T sum(double, int, short)
$ cat ~cs253/Example/mangle.cc void b(unsigned __int128, __int128, long double, unsigned char, long double, signed char, double) { } $ g++ -c ~cs253/Example/mangle.cc $ nm mangle.o 0000000000000000 T _Z1bonehead
auto
With C++14, the return type can be declared as auto
, which means
that the actual type will be deduced by what is returned. auto
is
not a type.
auto pi() { return 3.14159; } auto fact() { cout << "π ≈ " << pi() << '\n'; } int main() { fact(); return 0; }
π ≈ 3.14159
The auto
type deduction tolerates no ambiguity.
auto foo(bool b) { constexpr short default_value = 0; if (!b) return default_value; else return 42; } int main() { return foo(false); }
c.cc:6: error: inconsistent deduction for auto return type: 'short int' and then 'int'
By default, arguments are passed by value.
void foo(int n) { ++n; ++n; } int main() { int v = 42; foo(v); cout << v << '\n'; }
42
The variable n
gets a copy of v
.
A C programmer might pass by address:
void foo(int *n) { *n = 99; } int main() { int v = 42; foo(&v); cout << v << '\n'; }
99
Note that the actual parameter is &v
, the address of v
.
This is rarely the best solution. Use a reference, instead.
C++ can pass a variable by reference:
void foo(int &n) { ++n; ++n; } int main() { int v = 42; foo(v); cout << v << '\n'; }
44
The variable n
is an alias of v
. This is often implemented
as a pointer that gets *
applied automatically.
You can use a read-only pointer:
void foo(const int *n) { *n = 99; } int main() { int v = 42; foo(&v); cout << v << '\n'; }
c.cc:2: error: assignment of read-only location '* n'
You can use a read-only reference:
void foo(const int &n) { ++n; ++n; } int main() { int v = 42; foo(v); cout << v << '\n'; }
c.cc:2: error: increment of read-only reference 'n'
vector
, string
).
bool foo(string s) { // pass by value return s.empty(); } int main() { string s(10'000, 'x'); const auto start = clock(); for (int i=0; i<10'000'000; i++) foo(s); cout << (clock()-start)/1.0e6 << " seconds\n"; }
3.22942 seconds
We made ten thousand copies of a ten-million-byte string, and so copied 100 gigabytes. Slow! 🐌
bool foo(const string &s) { // pass by constant reference return s.empty(); } int main() { string s(10'000, 'x'); const auto start = clock(); for (int i=0; i<10'000'000; i++) foo(s); cout << (clock()-start)/1.0e6 << " seconds\n"; }
0.058684 seconds
No copies were made! 😎
int
, double
, char
, …) by value.
string
, vector
, struct
, …),
by constant reference.
double sqrt(double d); int count_spaces(const string &s); void get_physical_info(int &height, int &weight); void remove_spaces(string &s); void read_picture(Picture &pic, ostream &os); void write_picture(const Picture &pic, ostream &os);
Arrays are special. They’re always passed by reference:
void foo(int a[]) { a[0] = 911; } int main() { int numbers[] = {555-1212, 867-5309}; // dashes⁇ foo(numbers); cout << numbers[0]; }
911
This only applies to old-style C arrays,
not std::vector
or std::array
.