为什么c++没有虚构造函数?
当前回答
总结:c++标准可以为“虚拟构造函数”指定一种表示法和行为,这是相当直观的,而且编译器也不难支持,但是为什么在已经可以使用create() / clone()清晰地实现功能的情况下(见下文),还要专门为此做出标准更改呢?它远不如正在酝酿中的许多其他语言提案那么有用。
讨论
让我们假设一个“虚拟构造函数”机制:
Base* p = new Derived(...);
Base* p2 = new p->Base(); // possible syntax???
在上面的代码中,第一行构造了一个Derived对象,因此*p的虚拟调度表可以合理地为第二行提供一个“虚拟构造函数”。(本页上有几十个回答说“这个物体还不存在,所以虚拟构建是不可能的”,这些答案都是不必要的短视地关注即将被构建的物体。)
第二行假设符号new p->Base()请求动态分配和另一个派生对象的默认构造。
注:
the compiler must orchestrate memory allocation before calling the constructor - constructors normally support automatic (informally "stack") allocation, static (for global/namespace scope and class-/function-static objects), and dynamic (informally "heap") when new is used the size of object to be constructed by p->Base() can't generally be known at compile-time, so dynamic allocation is the only approach that makes sense it is possible to allocate runtime-specified amounts of memory on the stack - e.g. GCC's variable-length array extension, alloca() - but leads to significant inefficiencies and complexities (e.g. here and here respectively) for dynamic allocation it must return a pointer so memory can be deleted later. the postulated notation explicitly lists new to emphasise dynamic allocation and the pointer result type.
编译器需要:
通过调用隐式的虚拟sizeof函数或通过RTTI获得这些信息,找出Derived需要多少内存 调用operator new(size_t)来分配内存 调用Derived(),位置为new。
OR
为结合了动态分配和构造的函数创建额外的虚表项
因此,指定和实现虚拟构造函数似乎并不是不可克服的,但最重要的问题是:它如何比使用现有的c++语言特性更好?就我个人而言,我认为下面的解决方案没有任何好处。
' clone() '和' create() '
c++常见问题解答文档中记录了一个“虚拟构造函数”习语,包含了用于默认构造或复制构造一个新的动态分配对象的虚拟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()以接受参数,不过为了匹配基类/接口的虚函数签名,重写的参数必须精确匹配基类重载中的一个。有了这些用户提供的显式功能,添加日志记录、检测、更改内存分配等就变得很容易了。
其他回答
When a constructor is invoked, although there is no object created till that point, we still know the kind of object that is gonna be created because the specific constructor of the class to which the object belongs to has already been called. Virtual keyword associated with a function means the function of a particular object type is gonna be called. So, my thinking says that there is no need to make the virtual constructor because already the desired constructor whose object is gonna be created has been invoked and making constructor virtual is just a redundant thing to do because the object-specific constructor has already been invoked and this is same as calling class-specific function which is achieved through the virtual keyword. Although the inner implementation won’t allow virtual constructor for vptr and vtable related reasons.
Another reason is that C++ is a statically typed language and we need to know the type of a variable at compile-time. The compiler must be aware of the class type to create the object. The type of object to be created is a compile-time decision. If we make the constructor virtual then it means that we don’t need to know the type of the object at compile-time(that’s what virtual function provide. We don’t need to know the actual object and just need the base pointer to point an actual object call the pointed object’s virtual functions without knowing the type of the object) and if we don’t know the type of the object at compile time then it is against the statically typed languages. And hence, run-time polymorphism cannot be achieved. Hence, Constructor won’t be called without knowing the type of the object at compile-time. And so the idea of making a virtual constructor fails.
我们有,只是它不是一个构造函数:-)
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;
}
当人们问这样的问题时,我喜欢对自己说:“如果这真的是可能的,会发生什么?”我真的不知道这意味着什么,但我猜这可能与能够基于所创建对象的动态类型重写构造函数实现有关。
我看到了一些潜在的问题。首先,在调用虚构造函数时,派生类不会被完全构造,因此实现中存在潜在的问题。
其次,在多重继承的情况下会发生什么?你的虚构造函数可能会被多次调用,然后你需要有某种方法知道哪个被调用了。
第三,一般来说,在构造时,对象并没有完全构造虚拟表,这意味着需要对语言规范进行很大的更改,以允许在构造时就知道对象的动态类型。这将允许基类构造函数在构造时调用其他虚函数,使用未完全构造的动态类类型。
最后,正如其他人指出的那样,您可以使用静态的“create”或“init”类型函数实现一种虚拟构造函数,基本上与虚拟构造函数所做的事情相同。
我们不能简单地说…我们不能继承构造函数。因此没有必要将它们声明为虚拟的,因为虚拟提供了多态性。
与面向对象的语言(如Smalltalk或Python)不同,这些语言的构造函数是表示类的对象的虚拟方法(这意味着您不需要GoF抽象工厂模式,因为您可以传递表示类的对象,而不是自己创建),c++是一种基于类的语言,并且没有表示语言的任何构造的对象。该类在运行时不作为对象存在,因此不能对其调用虚方法。
这符合“不用就不用付钱”的理念,尽管我所见过的每个大型c++项目最终都实现了某种形式的抽象工厂或反射。