Nano Hash - криптовалюты, майнинг, программирование

Отрезать переопределенный метод путем приведения

У меня есть класс B, который публично наследуется от A:

class A {
    private:
    virtual void method();
}

class B : public A {
    private:
    void method();
}

Теперь мне нужно как-то вызвать исходный A::method() в B::method(), не вызывая конструктор копирования для A.
A определен в библиотеке, которую я пытаюсь расширить, поэтому я не могу изменить этот код (сделать метод защищенным для пример). Можно ли каким-то образом преобразовать this ptr в B::method() и вырезать переопределенный method?

Я использую внешний интерфейс, который вызывает A::method(). Этот интерфейс правильно вызывает мой переопределенный B::method(), но я не могу заставить вызов интерфейса в B::method() не генерировать переполнение стека.


  • См. Также stackoverflow.com/questions/2170688/private-virtual -method-in-c, предполагая, что авторы класса A пытались от этого защититься. 17.12.2014
  • Я думаю, что вы не столкнетесь ни с чем, кроме проблем с предложенным вами подходом. 1. Вероятно, не виртуальный 2. Вероятно, нет виртуального деструктора 3. Разработчики не предполагали, что этот метод будет переопределен или вызван вне его реализации (следовательно, частный и незащищенный). Похоже, вы пытаетесь сделать то, чего они не собирались делать. 17.12.2014
  • @cdhowie: они могут быть private, но их все еще можно переопределить. Если бы не столь редкая необходимость вызывать базовую версию для выполнения некоторых операций с ней, это было бы разумной идиомой (какое-то время я выступал за выполнение всех virtual функций private, но я понял, что это неправильно: вместо этого они должны быть protected). 17.12.2014
  • @ DietmarKühl Да, защищенный виртуальный - это то, что я всегда делал для виртуальных методов, которые не должны быть общедоступными. Тем не менее, приятно знать, что существует частная виртуальная опция (хотя я, вероятно, не буду ее использовать). 17.12.2014
  • Это знак method() не предназначен для явного вызова подклассами; вы можете нарушить состояние объекта, сделав это (что было бы плохо)! Если вы считаете, что method() действительно нуждается в защите, я бы связался с поставщиком библиотеки и / или просто изменил исходный код самостоятельно, если он вам доступен. 17.12.2014

Ответы:


1

Поскольку метод private нельзя вызвать квалифицированным, и переопределение нельзя отменить, вы не сможете вызвать метод private без другого объекта. Приведение объекта не будет иметь никакого эффекта, потому что способ обработки виртуальных функций является частью реального объекта.

В прошлом я выступал за создание всех virtual функций (кроме деструктора) private, но необходимость вызова версии базового класса на самом деле не такая уж редкость. Таким образом, virtual функции должны не быть private, а protected. Конечно, если интерфейс действительно выполняет свои virtual функции private, эту ошибку дизайна не может исправить пользователь этого интерфейса.

Видя, что ответы, пропагандирующие ужасные хаки (#define private protected), получают положительные голоса, я бы рекомендовал скорее полагаться на добавляемые функции-члены, не являющиеся virtual, не изменяя макет объекта, и отредактировать файл заголовка, чтобы добавить подходящую функцию:

class A {
private:
        virtual void method();
protected:
    void call_method() { this->A::method(); }
};

Это изменение имеет гораздо более локальный эффект, и его просто нельзя переносить. Однако он просто полагается на то, что макет объекта не изменяется путем добавления метода, отличного от virtual (inline), и спецификатора доступа. Никакие предупреждения на уровне языка или около того не будут затронуты.

17.12.2014
  • К счастью, по крайней мере, автор интерфейса может позже исправить эту ошибку способом, сохраняющим ABI (по крайней мере, я не знаю какого-либо ABI, где это не сохранит общедоступные (и защищенные) интерфейсы библиотек). 17.12.2014
  • @Deduplicator: ... и ваш несоответствующий хак в значительной степени полагается на то, что ABI никоим образом не меняется при использовании спецификатора доступа. Если мы говорим о несоответствующих хаках, я бы просто отредактировал заголовок и добавил функцию, отличную от virtual protected inline, которая вызывает функцию с подходящим квалификатором ... 17.12.2014
  • Ну, +1 за это предложение. 17.12.2014
  • @ DietmarKühl Меня интересует, почему это не соответствует требованиям. В какой части спецификации говорится об этом? 17.12.2014
  • @cdhowie: ты имеешь в виду, добавив участника выше? Это нарушение правила единого определения: A определяется иначе при компиляции измененного заголовка, чем при его компиляции для библиотеки. Я сейчас слишком ленив, чтобы найти главы и стихи. 17.12.2014
  • @ DietmarKühl Верно, хорошо. Я предполагаю, что добавление объявления друга к классу также нарушит ODR. 17.12.2014
  • @cdhowie: да. Любое изменение определения будет нарушением ODR. Маловероятно, что они повлияют на компоновку объекта и вызовут реальные проблемы, но неопределенное поведение может вести себя точно так, как вы ожидаете, а затем решит вести себя совершенно иначе, когда действительно важно, чтобы программное обеспечение работало правильно (например, когда продавец делает предположительно последнюю демонстрацию перед тем, как закрыть многомиллионную сделку: это не пойдет хорошо, чтобы хорошо объяснить, вы знаете, наша программа просто немного неопределенного поведения). 17.12.2014
  • @ DietmarKühl Да, я сам часто отстаиваю опасности UB - я просто хотел прояснить, что составляет нарушение ODR. 17.12.2014

  • 2

    Не существует соответствующего способа вызова частной функции в производном классе.

    По крайней мере, если нет friend или шаблона-члена, вы можете специализироваться на своем собственном частном типе, чтобы ниспровергнуть намерение, соблюдая букву закона.

    1. Тем не менее, есть #define private public-hack, который может сработать в вашей реализации.
      Однако это Undefined Behavior, так что не жалуйтесь, что он работает не везде. Вероятный режим отказа - это ошибка связывания, хотя теоретически он может быть сколь угодно странным.

    2. Намного более надежным было бы добавление невиртуального защищенного встроенного сервера пересылки, хотя это означает фактическое редактирование заголовка.

    3. # P5 #
      # P6 #
    17.12.2014
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..