为什么C++中没有虚构造函数?

304

C++为什么没有虚拟构造函数?


17
如果C++有虚构造函数,你会如何使用它们? - R Sahu
11
虚函数有助于动态绑定,这发生在运行时。对象是在运行时创建的,对象的创建需要构造函数。如果这个构造函数是虚拟的,那么会出现“狗追尾巴”的情况(狗代表可怜的编译器 :p)。 - gawkface
2
@RSahu:很可能,请求在C++中使用虚构造函数的人心中有一个想法,即它是一个复制构造函数,并且将根据复制构造函数的参数动态调用。这是有逻辑意义的,但是C++无法处理通过不是指针的实例进行虚拟分派,也不能处理暗示动态堆栈(或更糟的静态)内存的情况。 - Joshua
23个回答

6
当人们问这样的问题时,我会自问“如果这是可能的,会发生什么?”我并不确定这意味着什么,但我猜这可能与基于正在创建的对象的动态类型覆盖构造函数实现有关。
我看到了一些潜在的问题。首先,派生类在虚拟构造函数被调用时并没有完全构造好,因此存在实现上的潜在问题。
其次,在多重继承的情况下会发生什么?您的虚拟构造函数将被多次调用,您需要知道哪个被调用。
第三,通常情况下,在构造时对象并没有完全构建好虚拟表,这意味着需要对语言规范进行大量更改,以允许在构造时了解对象的动态类型。然后,基类构造函数可以在构造时调用其他虚拟函数,具有未完全构造的动态类类型。
最后,正如其他人指出的那样,您可以使用静态的“create”或“init”类型函数来实现一种虚拟构造函数,这些函数基本上做与虚拟构造函数相同的事情。

6

虚构造函数的概念并不太适用,因为对象类型是对象创建的先决条件,但它并不完全被否定。

GOF的“工厂方法”设计模式利用了虚构造函数的“概念”,在某些设计情况下非常方便。


5
面试答案是:虚函数指针和表是与对象相关的,而不是与类相关的。因此构造函数建立虚拟表格,因此我们不能拥有虚构造函数,因为在对象创建之前没有Vtable。

4

每个具有一个或多个“虚拟函数”的类都会创建一个虚拟表(vtable)。当创建这样一个类的对象时,它包含一个“虚拟指针”,该指针指向相应vtable的基地。每当有虚拟函数调用时,将使用vtable来解析函数地址。 构造函数不能是虚拟的,因为当执行类的构造函数时,在内存中没有vtable,也就是说还没有定义虚拟指针。因此,构造函数应始终是非虚拟的。


4
你也不应该在构造函数内调用虚函数。参见:http://www.artima.com/cppsource/nevercall.html 此外,我不确定你是否真正需要一个虚构造函数。你可以通过编写一个能根据所需参数构造对象的函数来实现多态构造。

3
我们不能继承构造函数,所以声明它们为虚拟的没有意义,因为虚拟提供了多态性。

2

在C++中,虚构造函数是不可能存在的。例如,您无法将构造函数标记为虚拟。请尝试以下代码:

#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;aClass 类型的指针 a 分配了一个 anotherClass 的内存。这会自动调用构造函数(在aClassanotherClass中)。因此,我们不需要将构造函数标记为虚拟的。因为当对象被创建时,它必须按照创建链(即先基类,然后派生类)的顺序进行。 但是,当我们尝试删除 delete a; 时,只会调用基类析构函数。因此,我们必须使用虚拟关键字来处理析构函数。 因此,虚拟构造函数不可能,但虚拟析构函数是可以的。谢谢。

对于虚析构函数,请尝试访问此链接。https://dev59.com/IHRB5IYBdhLWcg3w77on#15903538 。这可能会有所帮助。 - Tunvir Rahman Tusher

2
有一个非常基本的原因:构造函数实际上是静态函数,在C++中没有任何静态函数可以是虚函数。
如果你有很多C++经验,你会知道静态函数和成员函数之间的区别。静态函数与类相关联,而不是对象(实例),所以它们不会看到“this”指针。只有成员函数可以是虚函数,因为vtable-使“虚拟”工作的隐藏函数指针表-实际上是每个对象的数据成员。
现在,构造函数的工作是什么?这就在名称中-“T”构造函数初始化T对象,因为它们被分配。这自动排除了它成为成员函数的可能性!对象必须存在才能拥有“this”指针和vtable。这意味着即使语言将构造函数视为普通函数(出于相关原因,我不会深入讨论),它们也必须是静态成员函数。
一个很好的方法是看一下“工厂”模式,特别是工厂函数。它们做你想要的事情,你会注意到如果类T有一个工厂方法,它总是静态的。它必须是这样的。

1
构造函数不是静态的,这是确定无疑的事实。 - curiousguy
这显然是无意义的。A f(g);调用了一个复制构造函数。而且,它可能是一个成员函数的对象g。或者void foo(A a); ... foo(f);。在这里,我们需要构造一个新的A来调用foo,并且可以看到有一个对象它可能是一个成员函数 -- f - David Schwartz
构造函数不是静态的,要获得静态构造函数,我们必须独立初始化静态数据成员。 - Aravinda KS

2
虚拟机制只有在您拥有基类指针指向派生类对象时才起作用。构造函数对于调用基类构造函数有自己的规则,基本上是从基类到派生类。虚拟构造函数如何有用或被调用呢?我不知道其他语言怎么做,但我看不出虚拟构造函数如何有用甚至实现。构造必须已经发生才能使虚拟机制有任何意义,也必须已经发生构造才能创建vtable结构,这提供了多态行为的机制。

如果我们使用基类指针指向基类对象并使用它调用虚函数,那么这将是后期绑定。但是,这是否会是运行时多态性? - ajaysinghnegi

1
当调用构造函数时,虽然此时还没有创建对象,但我们仍然知道将要创建的对象类型,因为已经调用了所属类的特定构造函数。 与函数相关联的virtual关键字意味着将调用特定对象类型的函数。 因此,我的想法是不需要创建虚拟构造函数,因为已经调用了将要创建对象的期望构造函数,使构造函数虚拟化只是一件多余的事情,因为已经调用了特定对象的构造函数,这与通过virtual关键字实现的调用类特定函数相同。 尽管内部实现由于vptr和vtable相关原因不允许虚拟构造函数。


另一个原因是C++是一种静态类型语言,我们需要在编译时知道变量的类型。编译器必须知道类类型以创建对象。要创建的对象类型是编译时决定的。如果我们使构造函数虚拟,则意味着我们不需要在编译时知道对象的类型(这就是虚拟函数提供的)。我们不需要知道实际对象,只需要基本指针来指向实际对象调用指向对象的虚拟函数而不知道对象的类型),如果我们不知道对象的类型,则违反了静态类型语言的规则。因此,无法实现运行时多态性。因此,在不知道对象的类型的情况下,构造函数不会被调用。因此,使构造函数虚拟的想法失败了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接