简体   繁体   English

如何在C ++中实现纯虚函数

[英]How to implement pure virtual function in C++

I think the implementation of virtual function is talked about a lot. 我认为虚拟功能的实现已经谈了很多。 My question is what about pure virtual function? 我的问题是纯虚函数呢? However it is implemented? 但它实施了吗? In virtual table, how to tell it is a pure or non-pure? 在虚拟表中,如何判断它是纯粹的还是非纯粹的? What the difference between pure virtual function and virtual function with implementation? 纯虚函数和虚函数与实现有什么区别?

There is no usually no implementation difference between pure and non-pure virtual functions. 纯虚函数和非纯虚函数之间通常没有实现差异。 Provided a pure-virtual function is defined, it acts like any other virtual function. 如果定义了纯虚函数,它就像任何其他虚函数一样。 If it is not defined, it only causes a problem if it is explicitly called. 如果未定义,则仅在显式调用时才会导致问题。

There are two major differences in behaviour, but there is usually no impact on the implementation of the virtual function mechanism itself. 行为有两个主要差异,但通常对虚函数机制本身的实现没有影响。 The compiler must not allow you to construct an object of a type which has pure virtual functions that don't have a non-pure final override in their inheritance hierarchy and any attempt to make a virtual call to a pure virtual function directly or indirectly from an object's constructor or destructor causes undefined behaviour. 编译器不允许构造具有纯虚函数的类型的对象,这些虚函数在其继承层次结构中没有非纯的最终覆盖,并且任何尝试直接或间接地从纯虚函数进行虚拟调用对象的构造函数或析构函数导致未定义的行为。

virtual void foo() = 0; virtual void foo()= 0;

A pure virtual function is still a virtual function, so it would be in the vtable, but the compiler doesn't require an implementation for it, and will prohibit instantiation of the base class that declares the pure virtual. 纯虚函数仍然是虚函数,因此它将在vtable中,但编译器不需要实现它,并且将禁止实例化声明纯虚函数的基类。 Since you can still dereference pointers of type of the abstract base class, the virtual table has to have an entry, for polymorphism and runtime binding. 由于您仍然可以取消引用抽象基类类型的指针,因此虚拟表必须具有一个条目,用于多态和运行时绑定。

Does that help? 这有帮助吗?

This is not an answer, but rather a follow up to the comments to this answer above 这不是答案,而是对上述答案的评论的后续跟进

The C++ language defines how the virtual dispatch mechanism takes place during construction. C ++语言定义了构造期间虚拟调度机制的发生方式。 When instantiating an object in a hierarchy, the base constructor gets called (*). 在层次结构中实例化对象时,将调用基础构造函数(*)。 At that point, the virtual dispatch mechanism is initialized for the base class. 此时,为基类初始化虚拟调度机制。 At this stage, virtual functions will be dispatched to the base implementation. 在此阶段,将将虚函数分派给基本实现。 Any call to a non-pure virtual method using the virtual dispatch mechanism (without explicit class qualification) will call the base implementation. 使用虚拟调度机制(没有显式类限定)对非纯虚方法的任何调用都将调用基本实现。

After the base constructor is completed, the virtual dispatch mechanism (usually vtable) gets reseted to the derived type version, and any dynamic call from there on will call the derived version of the methods: 完成基本构造函数后,虚拟调度机制(通常是vtable)将重置为派生类型版本,并且从那里进行的任何动态调用都将调用方法的派生版本:

struct base {
   virtual void non_pure() { std::cout << "base::non_pure" << std::endl; }
   virtual void pure_not_implemented() = 0;
   virtual void pure_implemented() = 0;

   base() {                      // at this point the object is a ´base´
      non_pure();                // base::non_pure
      // pure_not_implemented(); // runtime error: pure virtual method called
      pure_implemented();        // base::pure_implemented
      // base::pure_not_implemented(); // link error
   }
};
void base::pure_implemented() { std::cout << "base::pure_implemented" << std::endl; }
struct derived : base {
   virtual void non_pure() { std::cout << "derived::non_pure" << std::endl; }
   virtual void pure_not_implemented() { std::cout << "derived::pure_not_implemented" << std::endl; }
   virtual void pure_implemented() { std::cout << "derived::pure_implemented" << std::endl;

   derived() {                // after the implicit call to the base class
                              //   this is a ´derived´ object, now calls will
                              //   get dispatched to derived:: implementations
      non_pure();             // derived::non_pure
      pure_not_implemented(); // derived::pure_not_implemented
      pure_implemented();     // derived::pure_implemented
      base::non_pure();       // base::non_pure
      // base::pure_not_implemented() // link error
   }
};

Note that there are differences between using the dynamic dispatch mechanism (usually vtable) and calling an specific method with the fully qualified name. 请注意,使用动态分派机制(通常是vtable)和使用完全限定名称调用特定方法之间存在差异。 A call to a non-implemented pure virtual method through the full qualification will be allowed at compile time but will fail at link time (the linker is not able to locate an implementation to call). 在编译时将允许通过完全限定调用未实现的纯虚方法,但在链接时将失败(链接器无法找到要调用的实现)。

This is an important decision in the design of the language. 这是语言设计中的一个重要决定。 The other option (the one Java took) is initializing the virtual dispatch mechanism to the most derived type from the beginning, previous to calling the base class constructor. 另一个选项(Java采用的那个)是在调用基类构造函数之前从一开始就将虚拟调度机制初始化为最派生类型。 The problem this approach has is that if the base constructor calls a virtual method (which in Java all are) it will get dispatched to the most derived implementation and will thus execute in a not-yet constructed object, possibly causing unexpected results: 这种方法的问题在于,如果基本构造函数调用一个虚方法(在Java中都是),它将被调度到最派生的实现,因此将在一个尚未构造的对象中执行,可能导致意外的结果:

public class Base
{
    public Base() {
        f();
    }
    public void f() {
        System.out.println("Base.f");
    }
}
public class Derived extends Base {
    public final int constant;
    public Derived() { constant = 5; }
    public void f() {
        System.out.println( "Derived.f() " + constant );
    }
    public static void main( String args[] ) {
        Derived d = new Derived();  // prints Derived.f() 0
    }
}

In the Java version, the dynamic dispatch mechanism considers the object to be of type Derived from the beginning. 在Java版本中,动态分派机制从一开始就将对象视为Derived类型。 The call to f() in the base constructor will be dynamically dispatched to the derived implementation. 基础构造函数中对f()的调用将动态调度到派生实现。 In the example above, even if the variable is declared as final and thus a constant by the value of 5 (seems obvious in the code), the actual printed value is 0 because the constructor of Derived has not executed. 在上面的例子中,即使变量被声明为final,因此值为5(在代码中显而易见),实际打印值为0,因为Derived的构造函数尚未执行。

(*) This is oversimplifying, but the details don't really affect the argument. (*)这是过于简单化,但细节并没有真正影响论点。

I do not know the actual implementation, but a good choice would be to implement it as a NULL pointer in the vtable . 我不知道实际的实现,但一个很好的选择是将它作为vtableNULL指针实现。 In other words, if you have an implementation there is a valid function pointer in vtable and if it is pure virtual, you have a NULL pointer. 换句话说,如果你有一个实现, vtable有一个有效的函数指针,如果它是纯虚vtable ,你有一个NULL指针。

This is so logical that I even think it is implemented this way:-). 这是合乎逻辑的,我甚至认为它是以这种方式实现的:-)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM