aJ.*_*aJ. 229

从马的嘴里听到它:).

来自Bjarne Stroustrup的C++风格和技巧FAQ 我们为什么不拥有虚拟构造函数?

虚拟调用是一种在给定部分信息的情况下完成工作的机制.特别是,"virtual"允许我们只知道任何接口而不是对象的确切类型来调用函数.要创建对象,您需要完整的信息.特别是,您需要知道要创建的内容的确切类型.因此,"对构造函数的调用"不能是虚拟的.

FAQ条目继续为代码提供了一种在没有虚构造函数的情况下实现此目的的方法.


Ant*_*lev 126

虚函数基本上提供多态行为.也就是说,当您使用动态类型与引用它的静态(编译时)类型不同的对象时,它提供的行为适合于实际的对象类型而不是对象的静态类型.

现在尝试将这种行为应用于构造函数.构造对象时,静态类型始终与实际对象类型相同,因为:

要构造一个对象,构造函数需要它创建的对象的确切类型[...]此外[...]你不能有一个指向构造函数的指针

(Bjarne Stroustup(P424 C++编程语言SE))

  • "_你不能拥有指向构造函数的指针""指向构造函数的指针"具有与空结构一样多的运行时信息,因为构造函数没有名称. (3认同)

Pet*_*ham 59

与面向对象的语言(如Smalltalk或Python)不同,其中构造函数是表示类的对象的虚方法(这意味着您不需要GoF 抽象工厂模式,因为您可以传递表示类的对象而不是制作C++是一种基于类的语言,并没有表示任何语言结构的对象.该类在运行时不作为对象存在,因此您无法在其上调用虚方法.

虽然我见过的每个大型C++项目最终都实现了某种形式的抽象工厂或反射,但这符合"你不为你不使用的东西付费"的理念.

  • 这恰恰是用C ++和像Delphi这样的语言(其中确实有虚拟构造函数)进行构造之间的区别。说得好。 (3认同)
  • 我想知道这个问题是否有意义,直到我读到对象创建如何在其他语言中工作的解释.+1. (3认同)
  • James Coplien的"高级C++"讲述了如何用C++实现虚拟构造函数(例如,新动物("dog")).有关如何实施的更多信息,请参见http://users.rcn.com/jcoplien/Patterns/C++Idioms/EuroPLoP98.html#VirtualConstructor (3认同)

use*_*637 41

我能想到的两个原因:

技术原因

该对象仅在构造函数结束后才存在.为了使用虚拟表调度构造函数,必须有一个带有指向虚拟表的指针的现有对象,但如果对象存在指向虚拟表的指针,该如何存在还不存在?:)

逻辑推理

如果要声明某种多态行为,可以使用virtual关键字.但是构造函数没有任何多态性,C++中的构造函数就是简单地将对象数据放在内存中.由于虚拟表(以及一般的多态)都是关于多态行为而不是多态数据,因此声明虚拟构造函数是没有意义的.


Mar*_*ius 14

除了语义原因之外,在构造对象之前没有vtable,因此使虚拟指定无用.

  • 错误.vtables是静态的和不变的.它们自加载可执行文件的代码和静态数据以来就存在. (3认同)
  • @Rich不。虚拟函数在构造函数中的工作方式与其他地方完全一样。**虚拟函数调用始终基于对象的动态类型。** (2认同)
  • @Rich否:在基类ctor中,构造函数中的虚拟调用将根据当时的动态类型动态地调用基类版本:基类。无论正在构造对象上的虚拟调用在ctor主体中还是在ctor进行的任何其他函数调用中,其工作方式都相同。基类子对象的动态类型随派生类的构造开始而变化。您只能通过打印`typeid(* this).name()`来看到。 (2认同)

小智 13

我们这样做,它不是一个构造函数:-)

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;
}
Run Code Online (Sandbox Code Playgroud)


Ton*_*roy 13

简介:C++标准可以为"虚拟构造函数"指定一种符号和行为,这种符号和行为相当直观,并且对于编译器来说并不太难以支持,但是为什么在使用/已经可以干净地实现该功能时对此进行标准更改(参见下面)?它不像管道中的许多其他语言提案那样有用.create()clone()

讨论

让我们假设一个"虚拟构造函数"机制:

Base* p = new Derived(...);
Base* p2 = new p->Base();  // possible syntax???
Run Code Online (Sandbox Code Playgroud)

在上面,第一行构造一个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++ 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(); }
Run Code Online (Sandbox Code Playgroud)

也可以更改或重载create()以接受参数,但是为了匹配基类/接口的virtual函数签名,覆盖的参数必须与基类重载之一完全匹配.使用这些显式的用户提供的工具,可以轻松添加日志记录,检测,更改内存分配等.


小智 6

C++中的虚函数是运行时多态的实现,它们将执行函数重写.通常,virtual当您需要动态行为时,关键字在C++中使用.它仅在对象存在时才有效.而构造函数用于创建对象.在创建对象时将调用构造函数.

因此,如果virtual按照虚拟关键字定义创建构造函数,它应该使用现有对象,但构造函数用于创建对象,因此这种情况永远不会存在.所以你不应该把构造函数用作虚拟.

所以,如果我们尝试声明虚构造函数编译器抛出一个错误:

构造函数不能声明为虚拟


Kus*_*hta 6

您可以在@stefan的答案中找到一个示例以及为什么不允许这样做的技术原因。现在,按照我的说法,对该问题的合乎逻辑的答案是:

当我们不知道基类指针将指向哪种类型的对象时,虚拟关键字的主要用途是启用多态行为。

但是请考虑一下这是一种更原始​​的方式,要使用虚拟功能,您将需要一个指针。指针需要什么?要指向的对象!(考虑程序正确执行的情况)

因此,我们基本上需要一个对象,该对象已经存在于内存中的某个位置(我们不关心内存的分配方式,它可能在编译时或在运行时),以便我们的指针可以正确指向该对象。

现在,考虑一下要指向的类的对象被分配一些内存的时刻的情况->它的构造函数将在该实例本身上自动调用!

因此,我们可以看到我们实际上不必担心构造函数是虚拟的,因为在任何情况下,您都希望使用多态行为,构造函数将已经执行,从而使我们的对象可以使用了!


180*_*ION 5

当人们问这样的问题时,我想对自己说:“如果这真的可能发生,会发生什么?” 我真的不知道这意味着什么,但是我想这与能够基于所创建对象的动态类型重写构造函数实现有关。

我看到与此有关的许多潜在问题。一方面,在调用虚拟构造函数时,派生类将无法完全构造,因此实现存在潜在问题。

其次,在多重继承的情况下会发生什么?您的虚拟构造函数可能会被多次调用,那么您将需要某种方式来知道哪个被调用。

第三,一般来讲,在构造时,对象没有完全构造虚拟表,这意味着需要对语言规范进行较大的更改,以允许在构造时就知道对象的动态类型。时间。然后,这将允许基类构造函数可以在构造时使用未完全构造的动态类类型调用其他虚函数。

最后,正如其他人指出的那样,您可以使用静态的“创建”或“初始化”类型的函数来实现一种虚拟构造函数,这些函数基本上可以执行与虚拟构造函数相同的操作。


小智 5

虽然虚拟构造函数的概念并不适合,因为对象类型是对象创建的先决条件,但它并不完全过度.

GOF的"工厂方法"设计模式利用了虚拟构造函数的"概念",在某些设计情况下可以使用.


skr*_*ngr 5

使用虚函数是为了根据指针所指向的对象的类型而不是指针本身的类型来调用函数。但是,不是“调用”构造函数。声明对象时仅调用一次。因此,不能在C ++中使构造函数虚拟化。


小智 5

面试答案是:virtual ptr和table与对象相关,但与类无关。因此构造函数构建虚拟表,因此我们不能有虚拟构造函数,因为在创建obj之前没有Vtable。


Edo*_* A. 4

您也不应该在构造函数中调用虚函数。请参阅: http: //www.artima.com/cppsource/nevercall.html

另外我不确定你是否真的需要一个虚拟构造函数。没有它你也可以实现多态构造:你可以编写一个函数来根据所需的参数构造你的对象。