aJ.*_*aJ. 229
从马的嘴里听到它:).
来自Bjarne Stroustrup的C++风格和技巧FAQ 我们为什么不拥有虚拟构造函数?
虚拟调用是一种在给定部分信息的情况下完成工作的机制.特别是,"virtual"允许我们只知道任何接口而不是对象的确切类型来调用函数.要创建对象,您需要完整的信息.特别是,您需要知道要创建的内容的确切类型.因此,"对构造函数的调用"不能是虚拟的.
FAQ条目继续为代码提供了一种在没有虚构造函数的情况下实现此目的的方法.
Ant*_*lev 126
虚函数基本上提供多态行为.也就是说,当您使用动态类型与引用它的静态(编译时)类型不同的对象时,它提供的行为适合于实际的对象类型而不是对象的静态类型.
现在尝试将这种行为应用于构造函数.构造对象时,静态类型始终与实际对象类型相同,因为:
要构造一个对象,构造函数需要它创建的对象的确切类型[...]此外[...]你不能有一个指向构造函数的指针
(Bjarne Stroustup(P424 C++编程语言SE))
Pet*_*ham 59
与面向对象的语言(如Smalltalk或Python)不同,其中构造函数是表示类的对象的虚方法(这意味着您不需要GoF 抽象工厂模式,因为您可以传递表示类的对象而不是制作C++是一种基于类的语言,并没有表示任何语言结构的对象.该类在运行时不作为对象存在,因此您无法在其上调用虚方法.
虽然我见过的每个大型C++项目最终都实现了某种形式的抽象工厂或反射,但这符合"你不为你不使用的东西付费"的理念.
use*_*637 41
我能想到的两个原因:
技术原因
该对象仅在构造函数结束后才存在.为了使用虚拟表调度构造函数,必须有一个带有指向虚拟表的指针的现有对象,但如果对象存在指向虚拟表的指针,该如何存在还不存在?:)
逻辑推理
如果要声明某种多态行为,可以使用virtual关键字.但是构造函数没有任何多态性,C++中的构造函数就是简单地将对象数据放在内存中.由于虚拟表(以及一般的多态)都是关于多态行为而不是多态数据,因此声明虚拟构造函数是没有意义的.
Mar*_*ius 14
除了语义原因之外,在构造对象之前没有vtable,因此使虚拟指定无用.
小智 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()通常无法知道要构造的对象的大小,因此动态分配是唯一有意义的方法
alloca()但是会导致显着的低效率和复杂性(例如,此处和此处分别)对于动态分配,它必须返回一个指针,以便delete以后可以使用内存.
假定的表示法明确列出new以强调动态分配和指针结果类型.
编译器需要:
Derived通过调用隐式virtual sizeof函数或通过RTTI获得此类信息,找出所需的内存量operator new(size_t)分配内存Derived()使用展示位置调用new.要么
所以 - 指定和实现虚拟构造函数似乎并不是不可克服的,但是价值数百万美元的问题是:如何使用现有的C++语言特性更好??就个人而言,我认为下面的解决方案没有任何好处.
在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按照虚拟关键字定义创建构造函数,它应该使用现有对象,但构造函数用于创建对象,因此这种情况永远不会存在.所以你不应该把构造函数用作虚拟.
所以,如果我们尝试声明虚构造函数编译器抛出一个错误:
构造函数不能声明为虚拟
您可以在@stefan的答案中找到一个示例以及为什么不允许这样做的技术原因。现在,按照我的说法,对该问题的合乎逻辑的答案是:
当我们不知道基类指针将指向哪种类型的对象时,虚拟关键字的主要用途是启用多态行为。
但是请考虑一下这是一种更原始的方式,要使用虚拟功能,您将需要一个指针。指针需要什么?要指向的对象!(考虑程序正确执行的情况)
因此,我们基本上需要一个对象,该对象已经存在于内存中的某个位置(我们不关心内存的分配方式,它可能在编译时或在运行时),以便我们的指针可以正确指向该对象。
现在,考虑一下要指向的类的对象被分配一些内存的时刻的情况->它的构造函数将在该实例本身上自动调用!
因此,我们可以看到我们实际上不必担心构造函数是虚拟的,因为在任何情况下,您都希望使用多态行为,构造函数将已经执行,从而使我们的对象可以使用了!
当人们问这样的问题时,我想对自己说:“如果这真的可能发生,会发生什么?” 我真的不知道这意味着什么,但是我想这与能够基于所创建对象的动态类型重写构造函数实现有关。
我看到与此有关的许多潜在问题。一方面,在调用虚拟构造函数时,派生类将无法完全构造,因此实现存在潜在问题。
其次,在多重继承的情况下会发生什么?您的虚拟构造函数可能会被多次调用,那么您将需要某种方式来知道哪个被调用。
第三,一般来讲,在构造时,对象没有完全构造虚拟表,这意味着需要对语言规范进行较大的更改,以允许在构造时就知道对象的动态类型。时间。然后,这将允许基类构造函数可以在构造时使用未完全构造的动态类类型调用其他虚函数。
最后,正如其他人指出的那样,您可以使用静态的“创建”或“初始化”类型的函数来实现一种虚拟构造函数,这些函数基本上可以执行与虚拟构造函数相同的操作。
您也不应该在构造函数中调用虚函数。请参阅: http: //www.artima.com/cppsource/nevercall.html
另外我不确定你是否真的需要一个虚拟构造函数。没有它你也可以实现多态构造:你可以编写一个函数来根据所需的参数构造你的对象。
| 归档时间: |
|
| 查看次数: |
223610 次 |
| 最近记录: |