Явный вызов конструктора и виртуальной функции во время жизни объекта

0

Мы можем вызвать конструктор с использованием имени с квалифицированным именем, хотя конструктор не имеет имени. Действительно, 3.4.3.2/2:

В поиске, в котором имена функций не игнорируются, а спецификатор вложенных имен назначает класс C:

- если имя, указанное после вложенного имени-спецификатора, при поиске на языке С, представляет собой введенное имя класса C (пункт 9) или

[...]

вместо этого имя считается названным конструктором класса C.

Рассмотрим следующий пример:

#include <iostream>

using std::cout;
using std::endl;

struct A
{
    virtual void foo()
    {
        cout << "A" << endl;
    }

    A(){ }
};

struct B : A
{
    virtual void foo()
    {
        cout << "B" << endl;
    }

    B()
    {
        foo();
    }  
};

struct C : B
{
    virtual void foo()
    {
        cout << "C" << endl;
    }

    C() : B(){ }      
};

C c;

int main()
{
    c.foo();
    C::C(); // Prints B
}

демонстрация

Строка C::C() печатает B. Но это неясно. В разделе 12.7/4 говорится:

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

В явном конструкторе вызов c уже полностью выполнен. Поэтому правило, которое я приводил, не может быть использовано для объяснения такого поведения. Это UB вообще? Не могли бы вы объяснить это?

  • 0
    Что делает C::C(); имеет отношение к объекту c ?
Теги:
constructor

2 ответа

1
Лучший ответ

Мы можем вызвать конструктор с использованием имени с квалифицированным именем, хотя конструктор не имеет имени.

Ваша предпосылка совершенно неверна. В том же абзаце, который вы указали (§3.4.3.1 [class.qual]/p2):

Такое имя конструктора должно использоваться только в идентификаторе-деклараторе объявления, которое называет конструктор или декларацию использования.

C::C(); объявление, которое называет конструктор? Нет. Это декларация использования? Очевидно нет.

Он плохо сформировался. По-видимому, Кланг считает, что вместо этого он назвал тип, по какой-то причине - вероятно, ошибкой (его обработка имен с введенными классами также является ошибкой в других отношениях).


Я также не знаю, как вы пришли к выводу, что поведение C::C(); - что, кстати, имеет смысл только в том случае, если вы считаете, что C::C чтобы назвать тип, может быть затронуто состоянием c, которое нигде не встречается в выражении или в какой-либо из соответствующих функций.

Гипотетический вызов явного конструктора объекта должен выглядеть как cC::C(); , потому что конструктор является нестатической функцией-членом. И не имеет никакого смысла разрешать вам вызывать конструктор на уже построенном объекте - что это значит?

  • 0
    Моей первой мыслью было, что C::C должен называть тип, поскольку C - это имя введенного класса , однако, как вы говорите, сам цитируемый текст говорит, что вместо этого считается, что он именует конструктор в этом контексте.
  • 0
    Это было мое невнимание. C::C() очевидно, не имеет никакого смысла. Но Clang не выдавал ошибку времени компиляции, и я предполагаю, что это правильно. Я понял, что нет способа явно конструировать вызов. Спасибо.
Показать ещё 1 комментарий
0

Линия...

C::C();

выводит...

B

потому что foo() вызывается изнутри конструктора B поэтому он будет использовать B версию foo().

Каждый раз, когда C вызывается конструктор, C::C() вызывает A конструктор, который вызывает B конструктор, в котором foo() называется так B версия foo() используется, а затем C тело конструктора (который вы оставили пустым), наконец, называется.


Пусть игнорирует тот факт, что вызов C::C() из main() не соответствует стандартам C++. Вы, кажется, неправильно интерпретируете часть раздела 12.7/4:

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

Вызов foo находится в B поэтому

... класс конструктора...

является B и поэтому

... не один переопределяет его в более производном классе.

(который был бы C).

Вы также указываете: "В явном вызове конструктора C уже полностью сконструирован". Линия...

C c; 

не изменит поведения, упомянутого в Разделе 12.7/4. foo() все еще называется конструктором B и поэтому

... вызванная функция является конечным переучителем в классе конструктора или деструктора.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню