Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The pointer-to-member operators .* and ->* are used to obtain an object or function as though it were a member of an underlying object. For instance, the following are functionally equivalent ways to call the member function f() on the object o:

Code Block
struct S {
  void f() {}
};

void func() {
  S o;
  void (S::*pm)() = &S::f;
  
  o.f();
  (o.*pm)();
}

The call of the form o.f() uses class member access at compile time to look up the address of the function S::f() on the object o. The call of the form (o.*pm)() uses the pointer-to-member operator .* to call the function at the address specified by pm. In both cases, the object o is the implicit this object within the member function S::f().

The C++ Standard, [expr.mptr.oper], paragraph 4, states [ISO/IEC 14882-2014]:

Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of E1 does

C++ 2003, Section 5.5 "Pointer-to-member operators", paragraph 4, says:

If the dynamic type of the object does not contain the member to which the pointer refers E2 refers, the behavior is undefined.

(A pointer-to-member expression of the form E1->*E2 is converted to its equivalent form, (*(E1)).*E2, so use of pointer-to-member expressions of either form behave equivalently in terms of undefined behavior.)

Do not So, trying to use a pointer-to-member operator to access a non-existent member leads to undefined behavior and must be avoided.

...

expression where the dynamic type of the first operand does not contain the member to which the second operand refers.

Noncompliant Code Example

In this non-compliant noncompliant code example there is an abstract base class Shape and a derived class Circle that contains a member function area. The last line of the code following the class definitions results in undefined behavior because there is no member function corresponding to area() in the class Shape., a pointer-to-member object is obtained from D::g, but upcast to be a B::*. When called on an object whose dynamic type is D, the pointer-to-member call is well-defined. However, in this noncompliant code example, the dynamic type of the underlying object is B, resulting in undefined behavior:

Code Block
bgColor#FFcccc
languagecpp
classstruct ShapeB {  // abstract
  // ...
public:
  virtual void draw~B() = 0;
  // ...default;
};

classstruct CircleD : public ShapeB {
  double radius;
public:
  Circle(double new_radius) : radius(new_radius) {}   
  void drawvirtual ~D() = default;
  virtual void g() {
    //* ...
 */ }
};

  virtual double areavoid f() {
  B *b = return PI*radius*radius;new B;
 
  }
};

// ...
 
Shape *circ = newvoid Circle(2.0);
double(Shape(B::*circ_areagptr)() = static_cast<doublecast<void(ShapeB::*)()>(&CircleD::areag);
cout << "Area: " << (circb->*circ_areagptr)() << endl;
}

Compliant Solution

...

In this compliant solution, the upcast is removed, rendering the initial code ill-formed. This emphasizes the underlying problem: that B::g() does not exist. This compliant solution assumed the programmer intent was to use the correct dynamic type for the underlying objectIf the developer is able to change the base class when it is realized that the area() method is required in the derived class, then a pure virtual area() method should be added to the class Shape:

Code Block
bgColor#ccccff
languagecpp
classstruct ShapeB {
  virtual ~B() // abstract
  // ...
public:= default;
};

struct D : B {
  virtual void draw~D() = 0default;
  virtual void areag() = 0;
 { //* ... */ }
}

Compliant Solution (Non-modifiable Base Class)

In many cases, the base class is not modifiable. In this case, one must call the derived method directly.

Code Block
bgColor#ccccff
Circle *circ = new Circle(2.0);
cout << "Area: " << (circ->area)() << endl;;

void f() {
  B *b = new D; // Corrected dynamic object type
 
  // ...
  void (D::*gptr)() = &D::g; // Removed static_cast  
  (static_cast<D *>(b)->*gptr)();
}

Risk Assessment

L2

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OBJ38OOP39-CPP

MediumHigh

Probable

MediumHigh

P8

P6

L2

Automated Detection

Tool

Version

Checker

Description

    

Related Vulnerabilities

Search for other vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

 

 

Bibliography

...

...

5.5, "Pointer-to-

...

Member Operators"

...

OOP37-CPP. Write constructor member initializers in the canonical order      013. Object Oriented Programming (OOP)      014. Concurrency (CON)