Сообщение Re: Вызов функции класса по ссылке от 01.12.2017 8:54
Изменено 01.12.2017 8:56 MasterZiv
Re: Вызов функции класса по ссылке
Здравствуйте, PavelCH, Вы писали:
PCH>struct ca
PCH>{
PCH> int a, b;
PCH> ca() : a(0), b(0)
PCH> {
PCH> }
PCH> void func()
PCH> {
PCH> a++;
PCH> }
PCH> typedef void(ca::*proc)();
PCH>};
PCH>struct cb : ca
PCH>{
PCH> void f2()
PCH> {
PCH> b++; c++;
PCH> }
PCH>};
PCH>} pdata[] = {
PCH> {"f2", (ca::proc)&cb::f2}, // Тут опасный код
PCH> {"func", &ca::func},
PCH>};
PCH>cb t1;
PCH>ca* p = &t1;
PCH>(p->*pdata[0].proc)(); // Есть ли какие-то подводные камни в таком вызове?
PCH>[/ccode]
Методы классов-наследников могут обращаться к данным классов-наследников (логично же),
а ты преобразуешь этот метод к указателю на (нестатический) метод класса-предка.
При вызове метода класса через указатель на метод класса ты обязан в вызове передать
ссылку на объект класса.
Указатель на метод класса-предка может быть вызван с любым объектом, являющимся
объектом класса-предка (ca в данном случае). Это значит, что это могут быть
объекты конечного класса ca, объекты конечного класс cb или любых наследников
(тут не определённых, но потенциально определяемых в будущем) от обоих этих классов,
т.е. как наследников ca, так и наследников cb (которые естественно являются наследниками и ca также).
При этом вызов методов класса cb для объектов разных конечных типов будет либо валидным, либо невалидным,
поскольку метод класса cb будет вызван с объектом, который НЕ является классом cb.
(нарушение принципа подстановки Лисков).
Для разных конечных классов объекта:
-- Класс ca -- вызов невалидный, поскольку реальный тип объекта — ca, а не cb
-- Класс cb -- вызов валидный, поскольку реальный тип объекта — cb
-- Класс -- какой-то наследник ca, но минуя в наследовании cb -- вызов невалидный, поскольку реальный тип объекта не cb
-- Класс -- какой-то наследник cb -- вызов валидный, поскольку реальный тип объекта cb
Соответственно, в части вариантов вызовов нарушается принцип подстановки и программа может быть невалидной.
Именно поэтому приведение указателя на метод или член класса-наследника к указателю на метод или член класса-предка
является неопределённым поведением (не проверял вот прямо сейчас в стандарте, но уверен, что именно так дело и обстоит).
Т.е. так писать программу нельзя.
Однако, если метод класса-наследника, который приведён к указателю на метод класса-предка, не обращается
к нестатическим данным класса-наследника и не вызывает нестатические методы класса-наследника, которые в свою очередь
могут иметь доступ к нестатическим данным класса-наследника, или после преобразования к указателю на
метод класса-предка через этот указатель метод будет вызываться только с объектами, являющимися объектами
класса-наследника, метод наследника может нормально работать.
PCH>struct ca
PCH>{
PCH> int a, b;
PCH> ca() : a(0), b(0)
PCH> {
PCH> }
PCH> void func()
PCH> {
PCH> a++;
PCH> }
PCH> typedef void(ca::*proc)();
PCH>};
PCH>struct cb : ca
PCH>{
PCH> void f2()
PCH> {
PCH> b++; c++;
PCH> }
PCH>};
PCH>} pdata[] = {
PCH> {"f2", (ca::proc)&cb::f2}, // Тут опасный код
PCH> {"func", &ca::func},
PCH>};
PCH>cb t1;
PCH>ca* p = &t1;
PCH>(p->*pdata[0].proc)(); // Есть ли какие-то подводные камни в таком вызове?
PCH>[/ccode]
Методы классов-наследников могут обращаться к данным классов-наследников (логично же),
а ты преобразуешь этот метод к указателю на (нестатический) метод класса-предка.
При вызове метода класса через указатель на метод класса ты обязан в вызове передать
ссылку на объект класса.
Указатель на метод класса-предка может быть вызван с любым объектом, являющимся
объектом класса-предка (ca в данном случае). Это значит, что это могут быть
объекты конечного класса ca, объекты конечного класс cb или любых наследников
(тут не определённых, но потенциально определяемых в будущем) от обоих этих классов,
т.е. как наследников ca, так и наследников cb (которые естественно являются наследниками и ca также).
При этом вызов методов класса cb для объектов разных конечных типов будет либо валидным, либо невалидным,
поскольку метод класса cb будет вызван с объектом, который НЕ является классом cb.
(нарушение принципа подстановки Лисков).
Для разных конечных классов объекта:
-- Класс ca -- вызов невалидный, поскольку реальный тип объекта — ca, а не cb
-- Класс cb -- вызов валидный, поскольку реальный тип объекта — cb
-- Класс -- какой-то наследник ca, но минуя в наследовании cb -- вызов невалидный, поскольку реальный тип объекта не cb
-- Класс -- какой-то наследник cb -- вызов валидный, поскольку реальный тип объекта cb
Соответственно, в части вариантов вызовов нарушается принцип подстановки и программа может быть невалидной.
Именно поэтому приведение указателя на метод или член класса-наследника к указателю на метод или член класса-предка
является неопределённым поведением (не проверял вот прямо сейчас в стандарте, но уверен, что именно так дело и обстоит).
Т.е. так писать программу нельзя.
Однако, если метод класса-наследника, который приведён к указателю на метод класса-предка, не обращается
к нестатическим данным класса-наследника и не вызывает нестатические методы класса-наследника, которые в свою очередь
могут иметь доступ к нестатическим данным класса-наследника, или после преобразования к указателю на
метод класса-предка через этот указатель метод будет вызываться только с объектами, являющимися объектами
класса-наследника, метод наследника может нормально работать.
Re: Вызов функции класса по ссылке
Здравствуйте, PavelCH, Вы писали:
PCH>struct ca
PCH>{
PCH> int a, b;
PCH> ca() : a(0), b(0)
PCH> {
PCH> }
PCH> void func()
PCH> {
PCH> a++;
PCH> }
PCH> typedef void(ca::*proc)();
PCH>};
PCH>struct cb : ca
PCH>{
PCH> void f2()
PCH> {
PCH> b++; c++;
PCH> }
PCH>};
PCH>} pdata[] = {
PCH> {"f2", (ca::proc)&cb::f2}, // Тут опасный код
PCH> {"func", &ca::func},
PCH>};
PCH>cb t1;
PCH>ca* p = &t1;
PCH>(p->*pdata[0].proc)(); // Есть ли какие-то подводные камни в таком вызове?
PCH>[/ccode]
Методы классов-наследников могут обращаться к данным классов-наследников (логично же),
а ты преобразуешь этот метод к указателю на (нестатический) метод класса-предка.
При вызове метода класса через указатель на метод класса ты обязан в вызове передать
ссылку на объект класса.
Указатель на метод класса-предка может быть вызван с любым объектом, являющимся
объектом класса-предка (ca в данном случае). Это значит, что это могут быть
объекты конечного класса ca, объекты конечного класс cb или любых наследников
(тут не определённых, но потенциально определяемых в будущем) от обоих этих классов,
т.е. как наследников ca, так и наследников cb (которые естественно являются наследниками и ca также).
При этом вызов методов класса cb для объектов разных конечных типов будет либо валидным, либо невалидным,
поскольку метод класса cb будет вызван с объектом, который НЕ является классом cb.
(нарушение принципа подстановки Лисков).
Для разных конечных классов объекта:
-- Класс ca -- вызов невалидный, поскольку реальный тип объекта — ca, а не cb
-- Класс cb -- вызов валидный, поскольку реальный тип объекта — cb
-- Класс -- какой-то наследник ca, но минуя в наследовании cb -- вызов невалидный, поскольку реальный тип объекта не cb
-- Класс -- какой-то наследник cb -- вызов валидный, поскольку реальный тип объекта cb
Соответственно, в части вариантов вызовов нарушается принцип подстановки и программа может быть невалидной.
Именно поэтому приведение указателя на метод или член класса-наследника к указателю на метод или член класса-предка
является неопределённым поведением (не проверял вот прямо сейчас в стандарте, но уверен, что именно так дело и обстоит).
Т.е. так писать программу нельзя.
Однако, если метод класса-наследника, который приведён к указателю на метод класса-предка, не обращается
к нестатическим данным класса-наследника и не вызывает нестатические методы класса-наследника, которые в свою очередь
могут иметь доступ к нестатическим данным класса-наследника, или после преобразования к указателю на
метод класса-предка через этот указатель метод будет вызываться только с объектами, являющимися объектами
класса-наследника, метод наследника может нормально работать.
PCH>struct ca
PCH>{
PCH> int a, b;
PCH> ca() : a(0), b(0)
PCH> {
PCH> }
PCH> void func()
PCH> {
PCH> a++;
PCH> }
PCH> typedef void(ca::*proc)();
PCH>};
PCH>struct cb : ca
PCH>{
PCH> void f2()
PCH> {
PCH> b++; c++;
PCH> }
PCH>};
PCH>} pdata[] = {
PCH> {"f2", (ca::proc)&cb::f2}, // Тут опасный код
PCH> {"func", &ca::func},
PCH>};
PCH>cb t1;
PCH>ca* p = &t1;
PCH>(p->*pdata[0].proc)(); // Есть ли какие-то подводные камни в таком вызове?
PCH>[/ccode]
Методы классов-наследников могут обращаться к данным классов-наследников (логично же),
а ты преобразуешь этот метод к указателю на (нестатический) метод класса-предка.
При вызове метода класса через указатель на метод класса ты обязан в вызове передать
ссылку на объект класса.
Указатель на метод класса-предка может быть вызван с любым объектом, являющимся
объектом класса-предка (ca в данном случае). Это значит, что это могут быть
объекты конечного класса ca, объекты конечного класс cb или любых наследников
(тут не определённых, но потенциально определяемых в будущем) от обоих этих классов,
т.е. как наследников ca, так и наследников cb (которые естественно являются наследниками и ca также).
При этом вызов методов класса cb для объектов разных конечных типов будет либо валидным, либо невалидным,
поскольку метод класса cb будет вызван с объектом, который НЕ является классом cb.
(нарушение принципа подстановки Лисков).
Для разных конечных классов объекта:
-- Класс ca -- вызов невалидный, поскольку реальный тип объекта — ca, а не cb
-- Класс cb -- вызов валидный, поскольку реальный тип объекта — cb
-- Класс -- какой-то наследник ca, но минуя в наследовании cb -- вызов невалидный, поскольку реальный тип объекта не cb
-- Класс -- какой-то наследник cb -- вызов валидный, поскольку реальный тип объекта cb
Соответственно, в части вариантов вызовов нарушается принцип подстановки и программа может быть невалидной.
Именно поэтому приведение указателя на метод или член класса-наследника к указателю на метод или член класса-предка
является неопределённым поведением (не проверял вот прямо сейчас в стандарте, но уверен, что именно так дело и обстоит).
Т.е. так писать программу нельзя.
Однако, если метод класса-наследника, который приведён к указателю на метод класса-предка, не обращается
к нестатическим данным класса-наследника и не вызывает нестатические методы класса-наследника, которые в свою очередь
могут иметь доступ к нестатическим данным класса-наследника, или после преобразования к указателю на
метод класса-предка через этот указатель метод будет вызываться только с объектами, являющимися объектами
класса-наследника, метод наследника может нормально работать.