Programming Language & Its Applications β C++ Constructs with Objects and Classes, NEC licence examination syllabus (Nepal Engineering Council).
A deliberate, class-controlled exception to private access β granted, never taken.
cout << myObject works for your own classes is a friend function. operator<< must take the stream as its left operand, so it cannot be a member of your class β yet it needs your private data to print it. Friendship is the only clean answer, and it is why virtually every serious C++ class you will read declares friend std::ostream& operator<<. Java has no equivalent and works around it with package-private access; C# uses internal. Search "why operator<< must be a non-member function" β the argument is short and it makes the whole feature click.#include <iostream>
class Account {
std::string owner;
double balance;
public:
Account(std::string o, double b)
: owner(std::move(o)), balance(b) {}
/* a single free function gets access */
friend void audit(const Account &a);
/* an entire class gets access */
friend class Auditor;
};
/* NOT a member β no Account:: prefix, no this */
void audit(const Account &a) {
std::cout << " audit: " << a.owner
<< " holds Rs " << a.balance << "\n";
}
class Auditor {
public:
/* every member of Auditor can see Account's privates */
static bool suspicious(const Account &a) {
return a.balance > 1000000;
}
static void adjust(Account &a, double delta) {
a.balance += delta; /* even writes */
}
};
int main() {
Account acc("Ram Bahadur", 50000);
audit(acc);
std::cout << " suspicious? " << std::boolalpha
<< Auditor::suspicious(acc) << "\n";
Auditor::adjust(acc, 2000000);
audit(acc);
std::cout << " suspicious? "
<< Auditor::suspicious(acc) << "\n";
return 0;
}
Output:
audit: Ram Bahadur holds Rs 50000
suspicious? false
audit: Ram Bahadur holds Rs 2050000
suspicious? true
#include <iostream>
#include <sstream>
class Complex {
double re, im;
public:
Complex(double r = 0, double i = 0) : re(r), im(i) {}
friend std::ostream& operator<<(std::ostream &os,
const Complex &c);
friend std::istream& operator>>(std::istream &is,
Complex &c);
/* a symmetric binary operator as a friend: both
operands are treated identically, which reads
better than making it a member */
friend Complex operator+(const Complex &a, const Complex &b);
friend bool operator==(const Complex &a, const Complex &b);
};
std::ostream& operator<<(std::ostream &os, const Complex &c) {
os << c.re << (c.im < 0 ? " - " : " + ")
<< (c.im < 0 ? -c.im : c.im) << "i";
return os; /* enables chaining */
}
std::istream& operator>>(std::istream &is, Complex &c) {
is >> c.re >> c.im;
return is;
}
Complex operator+(const Complex &a, const Complex &b) {
return Complex(a.re + b.re, a.im + b.im);
}
bool operator==(const Complex &a, const Complex &b) {
return a.re == b.re && a.im == b.im;
}
int main() {
Complex p(3, 4), q(1, -2);
/* chaining works because << returns ostream& */
std::cout << "p = " << p << "\nq = " << q
<< "\np + q = " << p + q << "\n";
std::cout << "p == q? " << std::boolalpha
<< (p == q) << "\n";
/* reading works too */
std::istringstream in("7 -8");
Complex r; in >> r;
std::cout << "read r = " << r << "\n";
return 0;
}
Output:
p = 3 + 4i
q = 1 - 2i
p + q = 4 + 2i
p == q? false
read r = 7 - 8i
this, and only the right side gets implicit conversion. For an operation that is mathematically symmetric, encoding that asymmetry in the design is simply wrong.
#include <iostream>
class B; /* forward declaration */
class A {
int secretA = 1;
public:
friend class B; /* B can see A's privates */
void tryPeek(const B &b); /* but can A see B's? */
};
class C {
int secretC = 3;
public:
friend class B; /* B is also C's friend */
};
class B {
int secretB = 2;
public:
void readA(const A &a) {
std::cout << " B reads A.secretA = "
<< a.secretA << "\n"; /* β granted */
}
void readC(const C &c) {
std::cout << " B reads C.secretC = "
<< c.secretC << "\n";
}
};
/* NOT SYMMETRIC: A is not B's friend, so this fails.
void A::tryPeek(const B &b) {
std::cout << b.secretB; error: 'int B::secretB' is
private within this context
} */
void A::tryPeek(const B &) {
std::cout << " A cannot read B β friendship is "
"one-directional\n";
}
/* NOT TRANSITIVE: B friends A and B friends C, but that
gives A no access to C. */
class Base {
int baseSecret = 10;
protected:
int protectedVal = 20;
public:
friend void showBase(const Base &b);
};
class Derived : public Base {
int derivedSecret = 30;
};
void showBase(const Base &b) {
std::cout << " friend of Base reads baseSecret = "
<< b.baseSecret << "\n";
}
/* NOT INHERITED: showBase is not a friend of Derived.
void f(const Derived &d) { std::cout << d.derivedSecret; }
error: private within this context */
int main() {
A a; B b; C c; Derived d;
b.readA(a);
b.readC(c);
a.tryPeek(b);
showBase(d); /* works: a Derived IS-A Base */
return 0;
}
Output:
B reads A.secretA = 1
B reads C.secretC = 3
A cannot read B β friendship is one-directional
friend of Base reads baseSecret = 10
internal plus InternalsVisibleTo for test assemblies, which is very close to friendship. And C++ has a cleverer modern option for the operator case: the hidden friend idiom, where you define the friend function inside the class body so it is only findable by argument-dependent lookup. Search "hidden friend idiom C++" β it improves compile times and avoids polluting the enclosing namespace.this, not called with the dot operator, and its placement in public/private/protected is irrelevant. The three non-properties (not symmetric, not transitive, not inherited) are asked almost verbatim. The operator<< example is the standard program, and be ready to explain why it cannot be a member. Finish with the encapsulation discussion: friendship is granted by the class, so it extends rather than breaks the boundary.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦