CS253: Software Development with C++

Spring 2022

Functions

Show Lecture.Functions as a slide show.

CS253 Functions

made at imgflip.com

Definition

Remember that a method is just a function associated with a class. Everything said here applies equally well to methods, because methods are functions.

Functions

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

Default Parameters

The trailing arguments can have default values:

int sum(int a, int b = 13) {
    return a+b;
}

int main() {
    cout << sum(2, 40) << '\n'
         << sum(8)     << '\n';
}
42
21
Are there two functions called sum, or just one?

There’s only one, which always takes two arguments. If you call sum with a single argument, the compiler adds a second argument of 13.

Default Parameters

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

Function Overloading

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
Are there three functions called sum, or just one?

Three. They’re right up there.

Function Overloading

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

For sum(double), the unused argument isn’t given a name, to avoid warnings about unused variables.

Implementation

Name Mangling

Return type doesn’t matter

int foo() { return 1; }
double foo() { return 2.0; }
int main() {
    return 0;
}
c.cc:2: error: ambiguating new declaration of ‘double foo()’

Name Mangling

$ 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++ -Wall -c ~cs253/Example/overload.cc

$ nm --numeric-sort overload.o 2>&-
0000000000000000 T _Z3sumii
0000000000000014 T _Z3sumi
000000000000002e T _Z3sumv
0000000000000039 T _Z3sumdis

$ nm --numeric-sort --demangle overload.o 2>&-
0000000000000000 T sum(int, int)
0000000000000014 T sum(int)
000000000000002e T sum()
0000000000000039 T sum(double, int, short)

More Name Mangling

$ cat ~cs253/Example/mangle.cc
void b(unsigned __int128,
       __int128,
       long double,
       unsigned char,
       long double,
       signed char,
       double) {
}

$ g++ -Wall -c ~cs253/Example/mangle.cc

$ nm mangle.o 2>&-
0000000000000000 T _Z1bonehead

I do not expect you to memorize the various encodings for types. It varies between compilers, anyway.

auto

// The “Golden Ratio”:
auto φ() {
    return (1+sqrt(5.0L))/2;
}

auto fact() {
    cout << "φ ≈ " << φ() << '\n';
}

int main() {
    fact();
    return 0;
}
φ ≈ 1.61803

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. It means “Compiler, you figure out the real type.”

What is the return type of fact?

void. It has no return statement.

What is the return type of φ?

The type of 5.0L is long double, so sqrt(5.0L) is long double, so 1+sqrt(5.0L) is long double, so (1+sqrt(5.0L))/2 is long double, so … long double.

Caution

The auto type deduction tolerates no ambiguity.

auto age_of_earth(bool b) {
    if (b)
        return 4.54e9;
    else
        return 6026;  // 🦡
}

int main() {
    return age_of_earth(false);
}
c.cc:5: error: inconsistent deduction for auto return type: ‘double’ and 
   then ‘int’

Pass by value

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 is a copy of v.

Pass by address

A C programmer might pass by address:

void foo(int *n) {
    *n = 99;
}

int main() {
    int v = 42;
    foo(&v);
    cout << v << '\n';
}
99

The actual parameter is &v, the address of v. This is fine C, but C++ can do better. Use a reference, instead.

Pass by reference

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.

Pass by constant address

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’

As before, avoid pointer arguments in favor of reference arguments.

Pass by reference

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’

Efficiency

Efficiency of pass by value

bool foo(vector<int> v) {    // 🦡 pass by value
    return v.empty();
}

int main() {
    vector<int> w(2500);
    for (int i=0; i<10'000'000; i++)
        foo(w);
    cout << "Done.\n";
}
Done.

Real time: 1.95 seconds

We made ten million copies of a ten-thousand-byte vector, and so copied 100 gigabytes. Slow! 🐌

Efficiency of pass by constant reference

bool foo(const vector<int> &v) {    // pass by constant reference
    return v.empty();
}

int main() {
    vector<int> w(2500);
    for (int i=0; i<10'000'000; i++)
        foo(w);
    cout << "Done.\n";
}
Done.

Real time: 6.38 ms

No copies were made! 😎

Rule of thumb

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

Arrays are special. They’re automatically 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.