Какие именно условия для динамического связывания?

class Foo
{
public:
void f() {}

virtual void g() {}
};

class Bar : public Foo
{
public:

void f() {}

virtual void g() {}
};

int main()
{
Foo foo;
Bar bar;

Foo *a = &bar;
Bar *b = &bar;

// case #1
foo.f();
foo.g();

// case #2
bar.f();
bar.g();

// case #3
a->f();
a->g();

// case #4
b->f();
b->g();
}

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

Но я не уверен насчет третьего и четвертого случаев. В третьем случае, по моему мнению, компилятор может знать типы, если он немного пытается угадать, куда указывает указатель. Так что может быть или не быть динамическое связывание.

В четвертом случае, будет ли указатель производного класса на объект производного класса по-прежнему включать динамическое связывание?

1

Решение

В общем, компиляторам C ++ разрешено агрессивно оптимизировать, если они следуют как будто править. То есть им разрешено оптимизировать любым способом, пока программа ведет себя как будто никакой оптимизации не произошло вообще1 — с точки зрения наблюдаемого и определенный поведение. (Если вы вызываете неопределенное поведение, тогда оптимизация вполне может изменить поведение, но поскольку поведение не было определено с самого начала, это не проблема.)

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

Чтобы конкретно ответить на ваш четвертый вопрос:

… будет ли указатель производного класса на объект производного класса по-прежнему включать динамическое связывание?

Если предположить, что компилятор не знать тип объекта, на который указывает, тогда да, он все равно должен включать динамическое связывание, если вы вызываете g() на Bar *, Это потому, что он может указывать на объект класса, который дополнительно выводит Bar типа — может быть, вы представите class Baz : Bar в другом модуле компиляции, или, может быть, даже какая-то сторонняя библиотека, загруженная в вашу программу, представляет это! Компилятор не будет знать, поэтому он должен предположить, что это мог произойдет, и поэтому он будет использовать динамическое связывание.

Однако, если компилятор может доказать, что Bar::g() не может быть отменено дальше, потому что это final2 или потому что Bar класс final тогда он может использовать (и, вероятно, будет использовать) статическую привязку при вызове g() на Bar *,


1 Стандарт имеет определенные исключения из этого правила. В частности, компилятору разрешено исключать копии объектов в некоторых случаях, что исключает вызовы конструкторов копирования, которые в противном случае были бы вызваны. Если бы у этих конструкторов были наблюдаемые побочные эффекты, тогда эта оптимизация изменила бы наблюдаемое поведение программы. Тем не менее, стандарт явно разрешает это, потому что копии могут быть очень дорогими (например, вектор с миллионом элементов) и потому что конструкторы копирования не должен есть побочные эффекты в любом случае.

2 final это новое ключевое слово в C ++ 11, которое предотвращает наследование класса или предотвращает переопределение виртуальной функции.

2

Другие решения

То, должно ли иметь место статическое или динамическое связывание, определяется тем, является ли функция виртуальной ИЛИ не виртуальной. Указатели и ссылки используются для получения желаемого поведения во время выполнения.

В случае простых объектов всегда есть статическая привязка.

Таким образом, в первых двух случаях это будет просто результат статического связывания. Эффект виртуального теряется, когда вы имеете дело с простыми объектами.

И в случаях 3 & 4, компилятор выводит статический тип из указателя, но он также видит функцию как виртуальную, поэтому он задерживает привязку к времени выполнения, когда фактический объект известен …

1

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

Типы в случае 1 & 2 может быть доказано, поэтому привязка может быть статической.

Это также возможно в 3 & 4, хотя это требует доказательства того, что a а также b не изменились, так как они были назначены.

Вещи стали бы интересными, если бы, скажем, мы прошли a или же b путем ссылки на внешне определенную функцию. На этом этапе нам нужна информация от переводчика а также компоновщик, чтобы определить, могли ли указатели измениться. Скорее всего, большинство компиляторов сдаются здесь и динамически связываются.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector