Russian Qt Forum
Ноябрь 23, 2024, 03:43 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Вызов private virtual метода  (Прочитано 13776 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Март 28, 2014, 16:01 »

Добрый день

Право, растерялся - если "private" то "извне" никак не вызвать. Однако простой пример ниже вызывает (печатается "А")
Код
C++ (Qt)
#include <stdio.h>
 
class A {
private:
virtual void Print( void ) { printf("A\n"); }
};
 
class B : public A {
public:
B( A & a ) : mA(a) {}
 
void Print( void ) { printf("B\n"); }
 
void Test( void )
{
B & b = static_cast <B &> (mA);
b.Print();
}
 
// data
A & mA;
};
 
int main(int argc, char *argv[])
{
A a;
B b(a);
b.Test();
 
return 0;
}
 
Please "ткните носиком" в статейки где это обсуждается
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Март 28, 2014, 16:11 »

Можно и короче
Код
C++ (Qt)
#include <stdio.h>
 
class A {
private:
virtual void Print( void ) { printf("A\n"); }
};
 
class B : public A {
public:
void Print( void ) { printf("B\n"); }
};
 
int main( void )
{
A a;
(static_cast <B &> (a)).Print();
 
return 0;
}
 
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #2 : Март 28, 2014, 16:20 »

Так вы же в наследнике (B) поменяли доступ на public.

Please "ткните носиком" в статейки где это обсуждается
Какие статейки, это описано в каждой книге, где автор только слышал про C++. Улыбающийся
« Последнее редактирование: Март 28, 2014, 16:31 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Март 28, 2014, 16:58 »

Так вы же в наследнике (B) поменяли доступ на public.

Какие статейки, это описано в каждой книге, где автор только слышал про C++. Улыбающийся
Неужели Непонимающий  Смотрим стандарт
Цитировать
If a class is declared to be a base class (clause 10) for another class using the public access specifier, the public members of the base class are accessible as public members of the derived class and protected members of the base class are accessible as protected members of the derived class.

If a class is declared to be a base class for another class using the protected access specifier, the public and protected members of the base class are accessible as protected members of the derived class.

If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class106).
Короче - квалификатор наследования может урезать права доступа (к базовому классу) но никогда их не расширит.

В примере выше мы не сможем написать A::Print() в методе класса B
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Март 28, 2014, 17:09 »

Не сможете.
Но здесь другой эффект. Компилятор проверяет права доступа, только в момент компиляции.
И здесь он видит, что вы пытаетесь вызвать метод print для объекта B.
Во время компиляции он не знает, что это на самом деле объект A. А при исполнении в vtbl этого объекта будет указатель на метод print объекта A.

А про стандарт. Что то вы путаете или ваш стандарт. Насколько я помню, мы можем менять доступ произвольно. Улыбающийся
Точнее вы читаете совсем про другое. Улыбающийся
« Последнее редактирование: Март 28, 2014, 17:27 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Март 28, 2014, 17:52 »

А про стандарт. Что то вы путаете или ваш стандарт. Насколько я помню, мы можем менять доступ произвольно. Улыбающийся
Точнее вы читаете совсем про другое. Улыбающийся
Неужели так сложно признаться "забыл, попутал" (или просто промолчать) - ведь это всего-навсего знание справочника Улыбающийся Вот еще
Цитировать
114) As specified previously in Clause 11, private members of a base class remain inaccessible even to derived classes unless friend declarations within the base class definition are used to grant access explicitly.

Не сможете.
Но здесь другой эффект. Компилятор проверяет права доступа, только в момент компиляции.
И здесь он видит, что вы пытаетесь вызвать метод print для объекта B.
Во время компиляции он не знает, что это на самом деле объект A. А при исполнении в vtbl этого объекта будет указатель на метод print объекта A.
В данном случае - прекрасно знает, "A" объявлен выше. Но не суть, мы можем вынести  это в ф-цию принимающую указатель/ссылку на A

Так что, выходит вызывать virtual private базового класса все-таки можно? Никогда не знал о таком. Или это все-таки некорректно  Непонимающий
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #6 : Март 28, 2014, 18:00 »

Неужели так сложно признаться "забыл, попутал" (или просто промолчать) - ведь это всего-навсего знание справочника Улыбающийся
А почему я должен это признавать? Я ничего не забыл.
Тот текст который вы привели повествует совсем о другом. Улыбающийся
Как вы это переводите? Улыбающийся
Цитировать
If a class is declared to be a base class

При написании класса мы можем определить метод в любой секции, куда захотим. Компилятору будет все равно, он будет проверять возможность доступа к методам. Поэтому, вызвать в производном классе private-метод базового мы не сможем, так же как и сделать ему using.
В вашем примере, вы не пытались добраться до метода A::print. Для компилятора вы определили новый публичный метод B::print, который по сигнатуре полностью совпал с виртуальным A::print. Должен ли компилятор предупреждать об этом - не знаю. Доступные мне компиляторы этого не делают.

Вот пример, как легко меняется доступ в любую сторону:
Код
C++ (Qt)
// Здесь Print public
class A {
public:
       virtual void Print() { printf("A\n"); }
};
 
// Здесь мы его запротектили
class B : public A {
protected:
       virtual void Print() { printf("B\n"); A::Print(); }
};
 
// Здесь мы его опять достали в паблик
class C : public B {
public:
       virtual void Print() { printf("C\n"); B::Print(); }
};
 
 
int main( void )
{
       A a;
       B b;
       C c;
 
       a.Print();
//      b.Print();    << B::Print недоступен
       c.Print();
 
       return 0;
}
 
Напечатает:
Цитировать
A
C
B
A

Но не суть, мы можем вынести  это в ф-цию принимающую указатель/ссылку на A
Покажите, что вы имеете ввиду.

Так что, выходит вызывать virtual private базового класса все-таки можно? Никогда не знал о таком. Или это все-таки некорректно  Непонимающий
Конечно можно, даже не виртуальные можно, таких способов полно. Только наберите в гугле и вы утонете в информации. Улыбающийся
Это все защита от ошибок, а не реальная защита от злоумышленников.
« Последнее редактирование: Март 28, 2014, 21:05 от Old » Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #7 : Март 28, 2014, 20:10 »

If a class is declared to be a base class (clause 10) for another class using the public access specifier, the public members of the base class are accessible as public members of the derived class and protected members of the base class are accessible as protected members of the derived class.

If a class is declared to be a base class for another class using the protected access specifier, the public and protected members of the base class are accessible as protected members of the derived class.

If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class106).
Короче - квалификатор наследования может урезать права доступа (к базовому классу) но никогда их не расширит.

Здесь речь идёт о дефолтном поведении для наследуемых методов, которые в явном виде не описаны в классе-наследнике. Если вы переопределяете виртуальный метод, то доступ можете установить ему какой хотите.
« Последнее редактирование: Март 28, 2014, 20:12 от Alex Custov » Записан
Akon
Гость
« Ответ #8 : Март 29, 2014, 10:15 »

Цитировать
Так что, выходит вызывать virtual private базового класса все-таки можно? Никогда не знал о таком. Или это все-таки некорректно
Я никогда не пользовался такой возможностью, даже не думал об этом. имхо, это некорректность из разряда: "нельзя иметь поля в классе, кроме как private".

А невирт. ф-ии таким образом вызвать нельзя.

Есть такой паттерн дизайна, как NVI (non-virtual interface). Подробно описан в книге Саттера "Сложные задачи" (название не дословное). Ксати, в той же книге есть рассмотрение вопросов, касающихся доступа. Основная суть NVI - паблик виртуальная функция - это слишком жирно, поскольку она определяет как интерфейс, так и реализацию (дает возможность изменить поведение). Поэтому, данный паттерн регламентирует иметь паблик невиртуальную функцию в базовом классе (определяет интерфейс) и привейт виртуальные функции в наследниках (определяют поведение). Если необходимо из наследника вызывать виртуальную функцию базового класса, то используется протектэд вместо привэйт.

Если для нового кода следовать этому паттерну, то об этой возможности вы даже не задумаетесь.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Март 29, 2014, 10:22 »

Если вы переопределяете виртуальный метод, то доступ можете установить ему какой хотите.
Для своего, переопределенного метода - конечно. Но private метод базового класса остается недоступным.
Код
C++ (Qt)
void MyFunc( const QSomeClass & a );
...
class QSоmeClass {
private:
 virtual void Method1( void );
 
Как мне вызвать QSomeClass::Method1 извне? Даже если protected (вместо private) - это часто капитальный геморрой. Надо переопределять класс (часто во многих местах), а главное - экземпляр QSomeClass может быть создан не моим кодом. Оказывается можно вызвать не создавая экземпляр

Но не суть, мы можем вынести  это в ф-цию принимающую указатель/ссылку на A
Покажите, что вы имеете ввиду.
Напр здесь компилятору ничего не известно кто такой "а" и он обязан использовать виртуальный механизм
Код
C++ (Qt)
void Test( A & a );
Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #10 : Март 29, 2014, 10:30 »

Это все хаки.
Самый простой это сделать:
#define private public

Вы описываете другой хак (умолчим, что такое преобразование опасно), есть ещё другие.
Защита доступа в C++ сделана для защиты от ошибок, а не для защиты от злоумышленников. Поэтому, она простая и работает на этапе компиляции.

Лично я хаками не пользуюсь.
Записан
Bepec
Гость
« Ответ #11 : Март 29, 2014, 13:04 »

Читал статейку и пробовал.
По сути private и public это защита от ошибок программиста, не более. И зная структуру класса можно вызвать что угодно и когда угодно, имея лишь его указатель.

PS защита от дурака в действии.
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #12 : Март 29, 2014, 14:16 »

По сути private и public это защита от ошибок программиста, не более. И зная структуру класса можно вызвать что угодно и когда угодно, имея лишь его указатель.

нет, public/private - это реализация одного из фундаментальных принципов, инкапсуляции. Но из-за того, что С++ решил быть расширенным Си, всё оказалось довольно криво и обходимо, как верно замечено про указатель на объект, даже в случае с PIMPL. Проблема только в том, что порядок полей и их смещения могут изменяться, и ваша программа перестанет работать.
Записан
Bepec
Гость
« Ответ #13 : Март 29, 2014, 14:49 »

Это если разные версии компиляторов и прочего. А в стабильной версии они стабильны. Хотя хз. 10 раз у меня получалось из 10. Мб на 100 и не получилось бы Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Март 29, 2014, 15:10 »

Разумеется если private/protected было объявлено - значит так надо и "снимать" его плохо/безыдейно. Но чисто технически я не вижу здесь никакого "хака", проблем со смещениями полей и.т.п. Класс "A" существует, класс "B" я объявляю сам. Где же "хак"?

Аналогии с "#define private public" неуместны, то всего лишь кромсание исходников (благо макруха это позволяет)
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.172 секунд. Запросов: 22.