The blog post introduces the friend, deleted, and defaulted functions in C++.

Friend Functions
When a function defined outside of a class needs to access the private members of that class, you can use friend functions.
class Private {
friend void double_value(Private& private);
private:
int value;
void add(int x) {
value += x;
};
public:
void print() {
cout << value << endl;
};
Private(int value) : value(value) {};
};
void double_value(Private& private) {
private.add(private.value);
};
int main () {
Private private(5);
double_value(private);
private.print(); // => 10!
return 0;
}
By using the friend
keyword to declare that the double_value
function is a friend function,
the double_value
function is granted access to the private attribute private.value
and
the private method private.add
.
A primary use case for friend functions is when overloading an operator, particularly when the left side of the operator is not an instance of the class itself. Operator overloading within a class assumes that the class instance appears on the left-hand side, but a friend function does not have that assumption.
class Counter {
friend Counter operator+(int n, Conuter counter);
private:
int count;
public:
Counter(int count) : count(count) {};
Counter operator+(int n) {
int new_count = count + n;
// The above assumes Counter is on the left.
return Counter(new_count);
}
};
Counter operator+(int n, Counter counter) {
int new_count = n + counter.count;
return Counter(new_count);
};
int main () {
Counter c1(1);
Counter c2 = 3 + c1; // c2.count = 4
Counter c3 = c2 + 3; // c3.count = 7
return 0;
}
Deleted Functions
By using the static
keyword, you can create a static member variable (or class-wide variable),
which can be useful, for example, in generating a unique ID.
class User {
static int next_id;
public:
int unique_id;
User() {
unique_id = next_id;
next_id++;
}
}
int User::next_id = 1;
int main () {
User user1;
User user2;
User user3;
cout << "User1 ID: " << user1.unique_id << endl;
cout << "User2 ID: " << user2.unique_id << endl;
cout << "User3 ID: " << user3.unique_id << endl;
// => User1 ID: 1
// User2 ID: 2
// User3 ID: 3
return 0;
}
However, the assignment operator and copy constructor are available by default,
which could allow the same unique_id
to be assigned to different instances.
int main () {
User user1;
User user2 = user1; // Copy Constructor
User user3;
user3 = user1; // Assignment Operator =
cout << "User1 ID: " << user1.unique_id << endl;
cout << "User2 ID: " << user2.unique_id << endl;
cout << "User3 ID: " << user3.unique_id << endl;
// => User1 ID: 1
// User2 ID: 1
// User3 ID: 1
return 0;
}
To prevent this, you can declare the copy constructor and assignment operator as deleted functions,
ensuring that these operations cannot be performed, thus preserving the uniqueness of unique_id
.
class User {
static int next_id;
public:
int unique_id;
User() {
unique_id = next_id;
next_id++;
}
User(User& user) = delete; // Copy Constructor Deleted
User& operator=(User& user) = delete; // Assignment Operator Deleted
}
Defaulted Functions
When you define a parameterized constructor, the default constructor of the class is automatically disabled.
class Counter {
public:
int count;
Counter (int count) : count(count) {};
};
int main () {
Counter counter; // Err!
return 0;
}
To enable the default constructor, you can use the default
keyword.
class Counter {
public:
int count;
Counter (int count) : count(count) {};
Counter () = default;
};
int main () {
Counter counter; // No Err
return 0;
}
Resources
- Portfolio Courses. 2022. Friend Functions | C++ Tutorial. YouTube.
- Portfolio Courses. 2022. Operator Overloading Using Friend Functions | C++ Tutorial. YouTube.
- Portfolio Courses. 2022. Deleted Functions | C++ Tutorial. YouTube.
- Portfolio Courses. 2022. Defaulted Functions | C++ Tutorial. YouTube.