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

Compare with Current View Page History

« Previous Version 15 Next »

It is often recommended that class objects be initialized using direct constructors rather than assignment. [Meyers 01] Direct constructors avoids construction, copying, and destruction of a temporary copy of the object. To wit, object should be constructed this way:

Widget w( /* constructor arguments */);

rather than this way:

Widget w = Widget( /* constructor arguments */);

or this way (for classes that support this syntax)

Widget w = /* constructor argument */;

Besides being inefficient, this last syntax violates OOP32-CPP. Ensure that single-argument constructors are marked "explicit".

However, C++ parsers are often liable to misparsing constructor arguments. While compilers will often generate a compiler error upon such misparses, it is possible for such misparses to slip past a compiler and lurk in executable code, with unexpected results.

Non-Compliant Code Example

In this non-compliant example, the class Widget has a default constructor.

class Widget {
public:
  explicit Widget() {cerr << "constructed" << endl;}
};

int main() {
  Widget w();
  return 0;
}

However, while a human may consider w to be explicitly built with the default constructor, the compiler interprets w to be a pointer to a function that takes no arguments, and returns a Widget!

As a result, this program compiles and prints no output, because the default constructor is never actually invoked.

Compliant Solution

This situation is ameliorated by removing the parentheses after w.

class Widget {
public:
  explicit Widget() {cerr << "constructed" << endl;}
};

int main() {
  Widget w;
  return 0;
}

Running this program produces the single output constructed.

Non-Compliant Code Example

Here is a more complex non-compliant example. The class Widget maintains a single int, and the class Gadget maintains a single Widget.

class Widget {
public:
  explicit Widget(int in) : i(in) {cerr << "widget constructed" << endl;}
private:
  int i;
};

class Gadget {
public:
  explicit Gadget(Widget wid) : w(wid) {cerr << "gadget constructed" << endl;}
private:
  Widget w;
};

int main() {
  int i = 3;
  Gadget g(Widget(i));
  cout << i << endl;
  return 0;
}

The declaration of g is not parsed as a Gadget with a 1-argument constructor. It is instead parsed as a pointer to a function that takes a single Widget argument, called i, and returns a Gadget. For illustrative purposes, keep in mind that in a function declaration, parentheses around argument names are optional. So the following is a legitimate function declaration, and indicates how the compiler sees the above declaration:

Gadget g(Widget i);

As a result, this program compiles cleanly and prints only 3 as output, because no Gadget or Widget is constructed.

Compliant Solution

This situation is ameliorated by moving the Widget construction outside Gadget.

int main() {
  int i = 3;
  Widget w(i);
  Gadget g(w);
  cout << i << endl;
  return 0;
}

Running this program produces the expected output:

widget constructed
gadget constructed
3

Risk Assessment

Not guarding implicit constructor parsing could lead to unexpected behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OOP31-CPP

low

likely

low

P9

L2

Bibliography

  • [Meyers 01] Item 6: Be alert for C++'s most vexing parse.

  • No labels