Constructor vs Init() vs Factory
TL;DR
There are a few ways to create a new object in a object-oriented programming languages.
- Constuctor: public constructors return fully constructed objects.
- Init: public trivial constructors return partially constructed objects + separate public
Init()method to finish initialization. - Factory method: private constructors + public static factory (e.g.
Create()) methods that return fully constructed objects.
https://abseil.io/tips/42 suggests "Prefer Factory Functions to Initializer Methods".
Constructors
Cons:
- they cannot fail except by crashing the entire program or throwing an exception. Not suitable for classes with complex initialization logic that may fail.
- they cannot safely call virtual methods of the object being constructed: if it calls virtual functions, these calls will not get dispatched to the subclass implementations.
- cannot take the address of a constructor, so whatever work is done in the constructor cannot easily be handed off to, for example, another thread.
public Init() method
Keep the constructors simple, and place complex or possibly-failing initialization in an Init() method
Pros:
- derived classes can perform possibly-failing initialization of their base classes.
Cons:
- the object is in a half-initialized state before
Init()has been called, which can lead to confusion about which methods can be called when. - it can be difficult to reverse the decision to use this design, because it imposes fewer restrictions on client code.
Conclusion:
Prefer factory functions unless there is some good reason to avoid heap allocation, or if the interface naturally lends itself to two-phase initialization.
Guidelines for using Init() methods:
- If
Init()is only needed so derived classes can initialize their base class, make itprotectedrather thanpublic, and express the public API in terms of factory functions. - If possible, have the constructor leave the object in a well-defined 'empty' or 'zero' state, where all methods work correctly even before initialization.
- If a 'zero' state is not possible, forbid all methods (or as many as possible) from being called before
Init(), enforce that rule, and provide a way for clients to determine if the object is initialized. - Pass any initialization data as parameters to
Init(), not as constructor parameters; avoid situations where an object may store pending-but-uninitialized data, as this is fertile ground for bugs. This has the useful side effect thatInit()may be called multiple times to reset the object to different values. - By the same token, avoid "setters", i.e. methods that set or modify the state of the object prior to initialization; in addition to the problem of pending-but-uninitialized data, these create the risk that a fully-initialized object may be invalidated by a setter called after initialization. If an API involving setter calls is more natural than passing the data all at once, consider using the Options class pattern or Builder pattern, which let you put these setter methods in a separate class.
- Failed initialization should leave the object unmodified; don't have separate states for "uninitialized" and "initialization failed". -->
Factory method
How:
- static
Create()function that returns the object (can return null on failure) - private constructor so it cannot be invoked directly Not copyable.
- Never return partially-initialized objects, or objects in an "error" state; factory functions should return a non-null result only on success.
- Make all constructors private or protected, so that all initialization goes through the factory functions.
- Try to do the initialization work prior to calling the constructor; it's easier to reason about objects that are fully-initialized as soon as their constructor returns.
Pros:
- Factory functions can return
nullto signal failure. - Factory functions can choose from different implementations, and may return different concrete implementation classes depending on the parameters
- Factory functions can have meaningful names, unlike constructors.
Cons:
- they can only construct objects on the heap, not suitable for lightweight, frequently-allocated, or value-like classes.
- cannot be invoked from a derived class constructor
// foo.h
class Foo {
public:
// Factory method: creates and returns a Foo.
// May return null on failure.
static std::unique_ptr<Foo> Create();
// Foo is not copyable.
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
private:
Foo(); // Clients can't invoke the constructor directly.
};
Foo foo = Foo::Create();
// foo.cc
std::unique_ptr<Foo> Foo::Create() {
// Foo's constructor is private, we have to use new. https://abseil.io/tips/134
return absl::WrapUnique(new Foo());
}
Recommended Reading
SponsoredVersus
- 32-bit vs 64-bit
- AES vs RSA
- Agent vs Agentless
- Annotations vs Decorators
- Terraform vs Ansible
- BASH vs ZSH
- BigQuery vs Bigtable
- Block Storage vs File Storage vs Object Storage
- Canvas vs SVG
- cgroupfs vs systemd
- Constructor vs Init() vs Factory
- Container Image vs Helm Chart vs OCI Bundle
- Containers vs Virtual Machines (VMs)
- C vs C++
- Java vs C++
- Rust vs C++
- crictl vs ctr
- Dagger vs Guice
- DMA vs RDMA
- DOM vs Virtual DOM vs Shadow DOM
- DQL vs DDL vs DCL vs DML
- eBPF vs Sidecar
- Flux vs Redux
- Full Disk Encryption vs Partition Encryption
- Gatekeeper vs OPA
- Google vs Open Source
- Why MCP uses JSON-RPC 2.0 instead of grpc
- gRPC vs OpenAPI
- gtag.js vs Google Tag Manager (GTM)
- Versus
- Kotlin vs Java
- Jetty vs Netty
- .js vs .jsx vs .ts vs .tsx
- Kata Containers vs KubeVirt
- Linux Namespace vs Kubernetes Namespace
- Linux vs BSD
- LLVM vs JVM
- Data Mining vs Machine Learning vs Artificial Intelligence vs Data Science
- Managed Kubernetes vs Self-managed Kubernetes
- Management Plane vs Control Plane vs Data Plane
- Microcontroller vs Microprocessor vs Computer
- MMU vs IOMMU
- Single-tenant vs Multi-tenant vs Multi-single-tenant
- Multithreading vs Multiprocessing vs Distributed Systems
- Node.js vs Erlang
- OCI Image vs Docker Image
- OCI vs CRI
- OpenSSH vs OpenSSL
- Pass-by-value vs Pass-by-reference
- Podman vs nerdctl
- POSIX vs SUS vs LSB
- Private Key vs Public Key vs Certificate
- Proto Edition 2024 vs Proto Edition 2023
- Proto2 vs Proto3 vs Proto Editions
- Message Queue vs PubSub
- React vs Flutter vs Angular
- REST vs SOAP
- RSA vs ECDSA
- select vs. poll vs. epoll
- SLI vs SLO vs SLA
- Spanner vs Bigtable
- Static Site Generation vs Server-side Rendering vs Client-side Rendering
- Stack based VM vs Register based VM
- Stateless vs Stateful
- Strong Consistency vs Eventual Consistency
- stubs vs mocks vs fakes
- Subroutines vs Coroutines vs Generators
- Software Engineer vs Site Reliability Engineer
- Symlinks vs Hard Links
- JAX vs Tensorflow
- Tensorflow vs PyTorch
- Terminal vs Shell
- Throttling vs Load Shedding
- Unix Domain Sockets vs Network Sockets
- Unix Domain Sockets vs Shared Memory
- URI vs URN vs URL vs URC
- Valkey vs Redis vs Memcached
- Vi vs Vim vs gVim vs Neovim
- WAL vs rollback journal

