← Назад к вопросам

Что такое приватное наследование?

2.3 Middle🔥 201 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое приватное наследование?

Приватное наследование (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 наследования за чистоту кода.

Что такое приватное наследование? | PrepBro