You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 25 Next »

Virtual functions enable a program to invoke a function appropriate to the object the function is being called on. If a virtual method is called on a pointer, the actual class of the pointed-to object is consulted at run-time, to determine the appropriate function to invoke. This contrasts with non-virtual functions, where the function invoked is determined by the compiler.

However, during the construction of an object, the rules for virtual invocation are different. C++ 2003, section 12.7 "Construction and Destruction", says:

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructor’s own class or one of its bases, the result of the call is undefined.

Because of this difference in behavior, it is recommended that you never invoke an object's virtual function while it is being constructed or destroyed.

Non-Compliant Code Example

Consider a design that attempts to seize and release an object's resources through calls to virtual functions from a base class's constructor and destructor.

class B {
  public:
    B() { seize(); }
    virtual ~B() { release(); }
  protected:
    virtual void seize() {}
    virtual void release() {}
};

class D : public B {
  public:
    D() {}
    ~D() {}
  protected:
    void seize() {
        B::seize();
        // get derived resources...
    }
    void release() {
        // release derived resources...
        B::release();
    }
};

D x;

The result of running this code is that no derived class resources will be seized or released during the initialization and destruction of the x object. At the time of the call to seize in the initialization of x, the D constructor has not been entered, and the behavior of the under-construction x object will be to invoke B::seize rather than D::seize. A similar situation occurs for the call to release in the base class destructor. If the functions seize and release were declared to be pure virtual functions, the result would be undefined behavior.

Compliant Solution

Avoid calling an object's virtual functions while it is being constructed or destroyed.

class B {
  public:
    B() {
        // seize base resources...
    }
    virtual ~B() {
        // release base resources...
    }
};

class D : public B {
  public:
    D() {
        // seize derived resources...
    }
    ~D() {
        // release derived resources...
    }
};

D x;

Note that it is perfectly legitimate to call virtual functions of other, fully constructed objects from a constructor or destructor.

Exceptions

OOP30-EX1: Since valid uses cases exist that involve calling (non-pure) virtual functions from the constructor of a class, it is recommended to explicitly qualify the name of the virtual function with the name of the class that defines it. This approach silences warnings from compilers such as HP aCC that diagnose violations of the rule. Note that calls to ordinary, non-virtual member functions that call virtual functions of the same class that aren't so qualified may be unsafe and thus lead to diagnostics. Thus, this exception does not extend to such cases.

class A {
  A() {
    // f();   // WRONG!
    A::f();   // okay
  }
  virtual void f();
};

Risk Assessment

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OBJ30-CPP

1 (low)

1 (unlikely)

2 (medium)

P2

L3

References

[[Dewhurst 03]] Gotcha 75: Calling Virtual Functions in Constructors and Destructors
[[Sutter 04]] Item 49: Avoid calling virtual functions in constructors and destructors.
[[Lockheed Martin 05]] AV Rule 71.1 A class’s virtual functions shall not be invoked from its destructor or any of its constructors.


OOP06-CPP. Create a private copy constructor and assignment operator for non copyable objects      13. Object Oriented Programming (OOP)      OOP33-CPP. Do not slice polymorphic objects

  • No labels