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

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

Ответ 1

Услышь это изо рта лошади. :)

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

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

Запись FAQ часто дает код для способа достижения этой цели без виртуального конструктора.

Ответ 2

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

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

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

(Bjarne Stroustup (P424 Язык программирования С++ SE))

Ответ 3

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

Это соответствует философии "вы не платите за то, что не используете", хотя каждый крупный проект С++, который я видел, закончил реализацию некоторой формы абстрактного factory или отражения.

Ответ 4

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

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

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

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

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

Ответ 5

Мы делаем, это просто не конструктор: -)

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;
}

Ответ 6

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

Ответ 7

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

Обсуждение

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

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

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

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

Примечания:

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

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

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

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

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

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

ИЛИ

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

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


"clone()" и "create()"

В C++ FAQ описывается идиома "виртуальный конструктор", содержащая методы 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 аргументы переопределений должны точно соответствовать одной из перегрузок базового класса. С помощью этих явных пользовательских средств легко добавить ведение журнала, инструментарий, изменить распределение памяти и т.д.

Ответ 8

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

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

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

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

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

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

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

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

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

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

Ответ 12

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

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

Ответ 16

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

Ответ 17

Здесь есть очень основная причина: конструкторы - это фактически статические функции, а в С++ статическая функция не может быть виртуальной.

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

Теперь, что такое задание конструктора? Он находится в имени - конструктор "Т" инициализирует объекты Т, поскольку они выделены. Это автоматически исключает его членскую функцию! У объекта должен быть EXIST, прежде чем он имеет указатель "this" и, следовательно, vtable. Это означает, что даже если язык обрабатывал конструкторы как обычные функции (это не так, по каким-то причинам я не буду вдаваться), они должны быть статическими функциями-членами.

Отличный способ увидеть это - посмотреть на шаблон "Factory", особенно factory. Они делают то, что вам нужно, и вы заметите, что если класс T имеет метод factory, он ВСЕГДА СТАТИЧЕСКИЙ. Это должно быть.

Ответ 18

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

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

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

#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;, это вызывает вызов только базового деструктора. Поэтому мы должны обрабатывать деструктор с использованием виртуального ключевого слова. Таким образом, виртуальный конструктор невозможен, но виртуальный деструктор.Thanks

Ответ 19

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

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

Ответ 20

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

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

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

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


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

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

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

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

Ответ 21

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