Вопрос

Почему в C++ нет виртуального конструктора?

Нет правильного решения

Другие советы

Услышьте это из первых уст :).

Из часто задаваемых вопросов по стилю и технике C++ Бьярна Страуструпа.Почему у нас нет виртуальных конструкторов?

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

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

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

Теперь попробуйте применить такое поведение к конструктору.Когда вы создаете объект, статический тип всегда совпадает с фактическим типом объекта, поскольку:

Чтобы создать объект, конструктору нужен точный тип объекта, который он должен создать [...] Кроме того, [...] у вас не может быть указателя на конструктор

(Бьерн Страуступ (P424 Язык программирования C++ SE))

В отличие от объектно-ориентированных языков, таких как Smalltalk или Python, где конструктор — это виртуальный метод объекта, представляющего класс (что означает, что вам не нужен GoF абстрактный фабричный узор, поскольку вы можете передавать объект, представляющий класс, вместо того, чтобы создавать свой собственный), C++ — это язык, основанный на классах, и в нем нет объектов, представляющих какие-либо конструкции языка.Класс не существует как объект во время выполнения, поэтому вы не можете вызвать для него виртуальный метод.

Это соответствует философии «вы не платите за то, что не используете», хотя каждый крупный проект C++, который я видел, заканчивался реализацией той или иной формы абстрактной фабрики или отражения.

две причины, о которых я могу думать:

Техническая причина

Объект существует только после завершения работы конструктора. Чтобы конструктор мог быть отправлен с использованием виртуальной таблицы, должен существовать существующий объект с указателем на виртуальную таблицу, но как может существовать указатель на виртуальную таблицу, если объект все еще не существует?:)

Логическая причина

Вы используете ключевое слово virtual, когда хотите объявить несколько полиморфное поведение.Но в конструкторах нет ничего полиморфного, задача конструкторов в C++ состоит в том, чтобы просто поместить данные объекта в память.Поскольку виртуальные таблицы (и полиморфизм в целом) связаны с полиморфным поведением, а не с полиморфными данными, нет смысла объявлять виртуальный конструктор.

Есть, просто это не конструктор :-)

struct A {
  virtual ~A() {}
  virtual A * Clone() { return new A; }
};

struct B : public A {
  virtual A * Clone() { return new B; }
};

int main() {

   A * a1 = new B;
   A * a2 = a1->Clone();    // virtual construction
   delete a2;
   delete a1;
}

Если оставить в стороне семантические причины, vtable не существует до тех пор, пока объект не будет создан, что делает виртуальное обозначение бесполезным.

Краткое содержание:стандарт С++ мог указать нотацию и поведение для «виртуальных конструкторов», которые достаточно интуитивно понятны и не слишком сложны для поддержки компиляторов, но зачем вносить для этого стандартные изменения, особенно когда функциональность уже может быть чисто реализовано с использованием create() / clone() (см. ниже)?Это далеко не так полезно, как многие другие языковые предложения, находящиеся в разработке.

Обсуждение

Постулируем механизм «виртуального конструктора»:

Base* p = new Derived(...);
Base* p2 = new p->Base();  // possible syntax???

В приведенном выше примере первая строка создает Derived объект, так что *pТаблица виртуальной диспетчеризации может разумно предоставить «виртуальный конструктор» для использования во второй строке.(Десятки ответов на этой странице, в которых говорится «объект еще не существует, поэтому виртуальное строительство невозможно» излишне близоруко сосредоточены на объекте, который предстоит построить.)

Вторая строка постулирует обозначение new p->Base() запросить динамическое размещение и создание по умолчанию другого Derived объект.

Примечания:

  • компилятор должен организовать распределение памяти перед вызовом конструктора - конструкторы обычно поддерживают автоматический (неформально «стек») распределение, статический (для глобальной области/пространства имен и класса/функции-static объекты) и динамичный (неофициально «куча»), когда new используется

    • размер объекта, который будет построен p->Base() обычно не может быть известно во время компиляции, поэтому динамическое размещение — единственный подход, который имеет смысл

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

  • постулированное обозначение явно перечисляет new чтобы подчеркнуть динамическое размещение и тип результата указателя.

Компилятору потребуется:

  • узнать сколько памяти Derived необходимо либо путем вызова неявного virtual sizeof функцию или наличие такой информации через RTTI
  • вызов operator new(size_t) выделить память
  • вызывать Derived() с размещением new.

ИЛИ

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

Итак, определение и реализация виртуальных конструкторов не кажется непреодолимым, но вопрос на миллион долларов:чем это было бы лучше, чем то, что возможно с использованием существующих функций языка C++...?Лично, Я не вижу никакой выгоды по сравнению с решением ниже.


`clone()` и `create()`

А Часто задаваемые вопросы по C++ документируют идиому «виртуального конструктора»., содержащий virtual create() и clone() методы для создания по умолчанию или копирования нового динамически выделяемого объекта:

class Shape {
  public:
    virtual ~Shape() { } // A virtual destructor
    virtual void draw() = 0; // A pure virtual function
    virtual void move() = 0;
    // ...
    virtual Shape* clone() const = 0; // Uses the copy constructor
    virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
  public:
    Circle* clone() const; // Covariant Return Types; see below
    Circle* create() const; // Covariant Return Types; see below
    // ...
};
Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }

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

Вы можете найти пример и техническую причину, по которой это запрещено, в ответе @stefan.По моему мнению, логичный ответ на этот вопрос:

Основное использование ключевого слова virtual — включить полиморфное поведение, когда мы не знаем, на какой тип объекта будет указывать указатель базового класса.

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

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

Теперь подумайте о ситуации, когда объекту класса, на который нужно указать, выделяется некоторая память -> Его конструктор будет вызван автоматически в этом экземпляре!

Итак, мы видим, что на самом деле нам не нужно беспокоиться о том, что конструктор виртуальный, потому что в любом случае, когда вы хотите использовать полиморфное поведение, наш конструктор уже был бы выполнен, что подготовило бы наш объект к использованию!

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

Шаблон проектирования «фабричный метод» GOF использует «концепцию» виртуального конструктора, которая удобна в определенных ситуациях проектирования.

Виртуальные функции в C++ представляют собой реализацию полиморфизма во время выполнения и переопределяют функции.Как правило, virtual Ключевое слово используется в C++, когда вам нужно динамическое поведение.Это будет работать только тогда, когда объект существует.Тогда как конструкторы используются для создания объектов.Конструкторы будут вызываться во время создания объекта.

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

Итак, если мы попытаемся объявить компилятор виртуального конструктора, выдаст ошибку:

Конструкторы не могут быть объявлены виртуальными.

Когда люди задают такой вопрос, мне нравится думать про себя: «Что произойдет, если бы это действительно было возможно?» Я действительно не знаю, что это будет означать, но я думаю, что это будет иметь какое -то отношение к возможности переопределить реализацию конструктора на основе динамического типа создаваемого объекта.

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

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

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

Наконец, как заметил кто-то другой, вы можете реализовать своего рода виртуальный конструктор, используя статические функции типа «создать» или «инициализировать», которые в основном делают то же самое, что и виртуальный конструктор.

Виртуальные функции используются для вызова функций на основе типа объекта, на который указывает указатель, а не типа самого указателя.Но конструктор не «вызывается».Он вызывается только один раз при объявлении объекта.Итак, конструктор в C++ нельзя сделать виртуальным.

Вам также не следует вызывать виртуальную функцию внутри вашего конструктора.Видеть : http://www.artima.com/cppsource/nevercall.html

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

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

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

Виртуальный механизм работает только тогда, когда у вас есть указатель базового класса на объект производного класса.Конструкция имеет свои собственные правила для вызова конструкторов базового класса, в основном базового класса для производного.Чем может быть полезен или вызван виртуальный конструктор?Я не знаю, что делают другие языки, но не понимаю, чем виртуальный конструктор может быть полезен или даже реализован.Необходимо, чтобы виртуальный механизм имел какой-либо смысл, и чтобы были созданы структуры vtable, обеспечивающие механику полиморфного поведения.

Есть очень простая причина:Конструкторы по сути являются статическими функциями, а в C++ ни одна статическая функция не может быть виртуальной.

Если у вас большой опыт работы с C++, вы знаете все о разнице между статическими функциями и функциями-членами.Статические функции связаны с КЛАССОМ, а не с объектами (экземплярами), поэтому они не видят указатель «this».Виртуальными могут быть только функции-члены, поскольку vtable — скрытая таблица указателей функций, обеспечивающая «виртуальную» работу — на самом деле является членом данных каждого объекта.

Итак, в чем же заключается работа конструктора?Это в названии: конструктор «T» инициализирует объекты T по мере их выделения.Это автоматически исключает возможность быть функцией-членом!Объект должен СУЩЕСТВОВАТЬ, прежде чем у него появится указатель «this» и, следовательно, виртуальная таблица.Это означает, что даже если бы язык рассматривал конструкторы как обычные функции (это не так, по связанным с этим причинам, которые я не буду вдаваться в подробности), они должны были бы быть статическими функциями-членами.

Отличный способ убедиться в этом — взглянуть на шаблон «Фабрика», особенно на фабричные функции.Они делают то, что вам нужно, и вы заметите, что если класс T имеет фабричный метод, то он ВСЕГДА СТАТИЧЕСКИЙ.Должно быть.

Виртуальный конструктор C++ невозможен. Например, вы не можете пометить конструктор как виртуальный. Попробуйте этот код.

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        virtual aClass()
        {   
        }  
};
int main()
{
    aClass a; 
}

Это вызывает ошибку. Этот код пытается объявить конструктор как виртуальный.Теперь давайте попробуем понять, почему мы используем ключевое слово virtual.Ключевое слово Virtual используется для обеспечения полиморфизма во время выполнения.Например, попробуйте этот код.

#include<iostream.h>
using namespace std;
class aClass
{
    public:
        aClass()
        {
            cout<<"aClass contructor\n";
        }
        ~aClass()
        {
            cout<<"aClass destructor\n";
        }

};
class anotherClass:public aClass
{

    public:
        anotherClass()
        {
            cout<<"anotherClass Constructor\n";
        }
        ~anotherClass()
        {
            cout<<"anotherClass destructor\n";
        }

};
int main()
{
    aClass* a;
    a=new anotherClass;
    delete a;   
    getchar(); 
}

В основном a=new anotherClass; выделяет память для anotherClass в указателе a заявлен как тип aClass.Это приводит к тому, что оба конструктора (In aClass и anotherClass) для автоматического вызова. Поэтому нам не нужно помечать конструктор как виртуальный. Потому что при создании объекта он должен следовать цепочке создания (т.е. сначала базовый, а затем производные классы).Но когда мы пытаемся удалить delete a; это приводит к вызову только базового деструктора. Поэтому нам приходится обрабатывать деструктор, используя ключевое слово virtual. Таким образом, виртуальный конструктор невозможен, но виртуальный деструктор возможен..Спасибо

Если вы логически подумаете о том, как работают конструкторы и каково значение/использование виртуальной функции в C++, то вы поймете, что виртуальный конструктор не имеет смысла в C++.Объявление чего-то виртуального в C++ означает, что оно может быть переопределено подклассом текущего класса, однако конструктор вызывается при создании объекта, в это время вы не можете создавать подкласс класса, вы должны быть создание класса, чтобы никогда не возникало необходимости объявлять виртуальный конструктор.

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

  1. Когда вызывается конструктор, хотя до этого момента не было создано ни одного объекта, мы все равно знаем, какой тип объекта будет создан, потому что конкретный конструктор класса, к которому принадлежит объект, уже был вызван.

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

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

    Хотя внутренняя реализация не позволяет использовать виртуальный конструктор по причинам, связанным с vptr и vtable.


  1. Другая причина заключается в том, что C++ — статически типизированный язык, и нам необходимо знать тип переменной во время компиляции.

    Для создания объекта компилятор должен знать тип класса.Тип создаваемого объекта определяется во время компиляции.

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

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

Vpointer создается во время создания объекта.vpointer не существует до создания объекта.поэтому нет смысла делать конструктор виртуальным.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top