HOWTO · C++
Understanding Empty Constructors in C++
Understand what an empty-bodied constructor means in C++, how it relates to default constructors, and how to initialize members safely.
On this page
An “empty constructor” usually means a user-written constructor with an empty body. It is a common description, not a separate category in the C++ language. The key question is whether a class has a default constructor: one that can be called with no arguments. That constructor may have parameters with default arguments, so “default constructor” does not necessarily mean a constructor declared with an empty parameter list.
An empty-bodied constructor does not automatically give every member a useful value. The member initialization rules depend on how the object is created and on the declarations in the class. Use an in-class member initializer or a member-initializer list when the class needs a reliable initial state.
Empty Constructors and Default Constructors
A default constructor is any constructor callable with no arguments. For example, Gauge(int level = 1) can be called without an argument and is therefore a default constructor. An empty-bodied constructor such as Gauge() {} is also a default constructor, but a constructor can do useful initialization in its body or in its member-initializer list.
C++ may implicitly declare a default constructor when a class has no user-declared constructors. Once you declare another constructor, the compiler does not also provide an implicit no-argument constructor for you. If objects should still be constructed without arguments, declare that overload explicitly or request the compiler-generated form with = default where appropriate. A defaulted constructor and an empty-bodied constructor are distinct declarations: the former asks the compiler to generate the operation, while the latter is a user-written function whose body does nothing.
The related default-constructor guide covers the broader rules for this constructor category. Here, the focus is the empty body and what it means for an object’s members.
Define an Empty Constructor
A no-argument constructor has the same name as its class and no return type. The program below gives value an in-class initial value. Creating meter calls the constructor, and the final line writes the member to standard output. The in-class initializer runs as part of constructing the object, before the constructor body. This complete program uses only standard C++ facilities and can be compiled on its own. The observed output follows.
Run this example:
#include <iostream>
struct Meter {
int value{7};
Meter() {}
};
int main() {
Meter meter;
std::cout << meter.value << '\n';
}
7
The declaration Meter() {} has an empty body; value{7} initializes the member before that body runs. Creating meter calls the no-argument constructor, and the program writes the member value. This example separates the constructor’s role from the member’s initialization source.
You can also initialize members in a member-initializer list, for example Meter() : value(7) {}. This is necessary for members that cannot be assigned after construction, such as reference members and const data members, and it is often the clearest place to establish invariants. Member initialization follows the order of member declarations in the class, regardless of the order in which names appear in the list.
What Happens to Members Without Initializers
An empty constructor body does not initialize scalar members. If a scalar such as int count; has no in-class initializer and is not listed in the member-initializer list, default-initializing an object leaves that member without an initialized value. Reading it before assigning a value is not safe. Do not rely on a later setter unless the class prevents every read before that setter is called.
An omitted member uses its default member initializer when one exists. Otherwise, it is initialized according to its type; a class-type member is default-initialized by selecting a default constructor. This does not mean all such members receive application-specific values. Check each member’s type and initialization requirements rather than assuming that an empty body creates a fully initialized object.
Default-initialization and value-initialization are different forms of initialization. For a class object, default-initialization calls the selected default constructor. Value-initialization can first zero-initialize the object when the selected default constructor is not user-provided, then perform default-initialization. A user-written constructor with {} is user-provided, so value-initializing that class does not make an otherwise uninitialized scalar member safe. These rules are easy to misread; an in-class initializer or member-initializer list is a more dependable way to express the intended state.
For example, struct Sample { int count{}; Sample() {} }; gives count the value zero through its in-class initializer. By contrast, struct Sample { int count; Sample() {} }; does not initialize count merely because the constructor exists. The braces in an empty function body are not member initializers.
When an Empty Constructor Is Useful
An empty-bodied constructor is reasonable when the class has no work to perform at construction time and every member already has an appropriate initialization rule. It can also make a deliberate public construction operation available to a framework or container that requires default construction. In either case, the class should be valid immediately after construction, rather than depending on callers to remember a sequence of setter calls.
Prefer in-class member initializers for simple defaults shared by constructors. Use a member-initializer list when the initial value depends on constructor arguments, when initializing const or reference members, or when a member type requires a particular constructor. If setup requires validation, resource acquisition, or a class invariant, put that work in a suitable constructor instead of leaving the body empty.
Collections and arrays may require elements to be default-constructible depending on the operation being used. An explicit no-argument constructor can satisfy that requirement, but it does not make deferred initialization safe by itself. Consider whether a factory, constructor arguments, or a container operation that constructs elements in place better communicates the required state.
Common Mistakes and Safer Alternatives
Do not equate “empty body” with “all members get default values.” A scalar without an initializer can remain indeterminate under default-initialization. Give it an in-class initializer, initialize it in the member-initializer list, or require a value through constructor parameters.
Do not assume that declaring a parameterized constructor leaves a no-argument constructor available. If both forms are needed, declare both, or explicitly default the no-argument form when its generated behavior is suitable. Constructor availability is determined by declarations and language rules, not by the order in which constructors appear in the class.
Finally, avoid creating an object in an incomplete state just to fill it through setters later. If the class has required data, take it in the constructor and establish the invariant there. Keep an empty-bodied constructor only when its no-op body is intentional and the member declarations themselves provide a valid initial state.