Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое приватное наследование?
Приватное наследование (private inheritance) — это форма наследования в C++, при которой все public и protected члены базового класса становятся private в производном классе. Это отличается от public наследования, где эта иерархия сохраняется. Приватное наследование обычно используется для реализации отношения "has-a" вместо "is-a".
Public vs Private наследование
class Animal {
public:
void speak() { std::cout << "Sound\n"; }
protected:
void breathe() { }
private:
void digest() { }
};
// PUBLIC наследование ("is-a")
class Dog : public Animal {
// speak() остаётся public
// breathe() остаётся protected
// digest() остаётся private
};
Dog dog;
dog.speak(); // OK - public
// PRIVATE наследование ("has-a")
class Puppy : private Animal {
// speak() становится private
// breathe() становится private
// digest() остаётся private
};
Puppy puppy;
puppy.speak(); // ОШИБКА! Методы базового класса недоступны снаружи
Таблица видимости
Base class Public derived Private derived
(видимость) (видимость) (видимость)
─────────────────────────────────────────────────────────────
public public public private
protected protected protected private
private private private private
Когда использовать приватное наследование?
Пример 1: Переиспользование кода (Composition)
// Плохо: public наследование
class Logger : public File {
// Logger "is-a" File? Нет! Logger "has-a" File
};
Logger logger;
logger.seek(100); // Зачем это методу логгера?
// Хорошо: private наследование
class Logger : private File {
public:
void log(const std::string& msg) {
// Используем методы File внутри
write(msg);
}
};
Logger logger;
logger.log("Error"); // OK
logger.seek(100); // Ошибка! Правильно!
Пример 2: Adapter паттерн
// Старый интерфейс
class OldDatabase {
public:
void query(std::string sql) { }
};
// Новый интерфейс адаптирует старый
class DatabaseAdapter : private OldDatabase {
public:
void execute(const std::string& sql) {
query(sql); // Используем старый метод внутри
}
};
DatabaseAdapter db;
db.execute("SELECT * FROM users"); // OK
db.query("SELECT * FROM users"); // Ошибка! query недоступен
Пример 3: EBO (Empty Base Optimization)
template <typename T>
class SmartPointer : private T {
private:
T* ptr;
public:
// SmartPointer переиспользует памяти T для оптимизации
};
Private наследование vs Composition
Чаще всего лучше использовать composition (содержание объекта):
// Private наследование
class Logger : private File { };
// Эквивалент: composition (предпочтительнее)
class Logger {
private:
File file_;
public:
void log(const std::string& msg) {
file_.write(msg);
}
};
Композиция лучше потому что:
- Явнее показывает отношение "has-a"
- Легче пробросить разные реализации File
- Нет конфликтов с несколькими базовыми классами
- Более читаемо для других разработчиков
Protected наследование
Редко используется, промежуточный вариант:
class Animal { };
class Dog : protected Animal { }; // protected наследование
class ServiceDog : public Dog { }; // ServiceDog видит Animal как protected
ServiceDog sd;
sd.speak(); // Ошибка! speak() protected для ServiceDog
class Puppy : public Dog {
protected:
void use_animal_method() {
speak(); // OK! Это protected для Puppy
}
};
Практический пример: Thread Pool
class ThreadPoolImpl {
private:
std::vector<std::thread> threads_;
std::queue<std::function<void()>> task_queue_;
public:
void submit(std::function<void()> task) {
task_queue_.push(task);
}
};
// Приватное наследование: скрываем детали реализации
class ThreadPool : private ThreadPoolImpl {
public:
template<typename Func>
void submit(Func func) {
ThreadPoolImpl::submit(func);
}
};
ThreadPool pool;
pool.submit([] { std::cout << "Task" << std::endl; });
// ThreadPoolImpl детали скрыты от пользователя
Недостатки приватного наследования
class Dog : private Animal { };
Dog dog;
Animal* ptr = &dog; // ОШИБКА! Нельзя преобразовать private inherited
// С public наследованием это работает (нужно для полиморфизма)
class Cat : public Animal { };
Cat cat;
Animal* ptr = &cat; // OK! Базовый класс видимо public
Правило в стандарте C++
Используй public наследование для "is-a" отношения (полиморфизм). Используй private наследование (или лучше composition) для "has-a" отношения.
Итог для собеседования
Основное: Private наследование скрывает базовый класс от внешнего кода, позволяя переиспользовать его реализацию внутри. Это реализация паттерна Adapter или Decorator.
Вместо: В современном C++ предпочитают composition вместо private наследования за чистоту кода.