Private и protected — это когда объект скрывает, что унаследован от студента. Снаружи не виден ни факт наследования, ни отцовские поля.
class Father {};
class Son : private Father {};
int main()
{
Son son;
Father& q = son;
}
error: 'Father' is an inaccessible base of 'Son'
Private и protected — скорее «хаки» и пользоваться ими не рекомендуется. Хотя, разумеется, пользуются, чтобы упростить себе жизнь. Я вижу два назначения: 1) хорошая основа для совершенно постороннего класса; 2) реализация интерфейса, которая нужна только внутри.
Вот пример второго. Объект FileWriter реализует интерфейс Callback для своих внутренних нужд.
#include <iostream>
class Callback {
public:
virtual void act(int x) = 0;
};
void generateFibonacci(int n, Callback& callback) {
int x1 = 0, x2 = 1;
for (int i = 0; i < n; ++i) {
callback.act(x2);
int x3 = x1 + x2;
x1 = x2;
x2 = x3;
}
}
class FileWriter : private Callback {
public:
FileWriter(std::ostream& aOs, int aN) : os(aOs), n(aN) {}
void run() { generateFibonacci(n, *this); }
private:
std::ostream& os;
const int n;
void act(int x) override { os << x << std::endl; }
};
using namespace std;
int main()
{
FileWriter wri(std::cerr, 10);
wri.run();
}
А если реальная жизнь — то объект может быть одновременно QDialog (диалоговое окно) и QAbstractItemModel (модель данных для какой-нибудь таблицы, лежащей на этом окошке). Хотя, повторяю, это скорее хак.
P.S. В Delphi и Java всё наследование публичное, и если нужно скрытно реализовать какой-то интерфейс или задействовать удобный класс — то только скрытым полем. Так, как в комментариях.
P.P.S. Пример первого. Какой-нибудь Array2d скрыто наследуется от Array1d, потому что у него совершенно другой интерфейс. У Array1d — alloc(n) и operator()(i), у Array2d — alloc(m, n) и operator()(i, j). А Array1d — неплохая штука, чтобы управляться блоком памяти длиной m×n элементов.