In software engineering, Creational Design Patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic or ordinary form of object creation could result in design problems or added complexity to the design. Builder Design Pattern in C++ solves this specific problem by separating the construction of a complex object from its representation.
By the way, If you haven’t check out my other articles on Creational Design Patterns, then here is the list:
  1. Factory
  2. Builder
  3. Prototype
  4. Singleton
The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like override, final, public(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing “public:” sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally.
I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.
Note:

Intent

To create/instantiate complex & complicated object piecewise & succinctly by providing an API in a separate entity.

Life Without Builders

// <p>hello</p>
auto text = "hello";
string output;
output += "<p>";
output += text;
output += "</p>";
printf("<p>%s</p>", text);

// <ul><li>hello</li><li>world</li></ul>
string words[] = {"hello", "world"};
ostringstream oss;
oss << "<ul>";
for (auto w : words)
    oss << "  <li>" << w << "</li>";
oss << "</ul>";
printf(oss.str().c_str());

Builder Design Pattern Example in Modern C++

class HtmlBuilder;

class HtmlElement {
    string                      m_name;
    string                      m_text;
    vector<HtmlElement>         m_childs;
    constexpr static size_t     m_indent_size = 4;

    HtmlElement() = default;
    HtmlElement(const string &name, const string &text) : m_name(name), m_text(text) {}
    friend class HtmlBuilder;

public:
    string str(int32_t indent = 0) {
        ostringstream oss;
        oss << string(m_indent_size * indent, ' ') << "<" << m_name << ">" << endl;

        if (m_text.size()) oss << string(m_indent_size * (indent + 1), ' ') << m_text << endl;

        for (auto &element : m_childs)
            oss << element.str(indent + 1);

        oss << string(m_indent_size * indent, ' ') << "</" << m_name << ">" << endl;
        return oss.str();
    }
    static unique_ptr<HtmlBuilder> build(string root_name) { return make_unique<HtmlBuilder>(root_name); }
};

class HtmlBuilder {
    HtmlElement     m_root;

public:
    HtmlBuilder(string root_name) { m_root.m_name = root_name; }
    HtmlBuilder *add_child(string child_name, string child_text) {
        m_root.m_childs.emplace_back(HtmlElement{child_name, child_text});
        return this;
    }
    string str() { return m_root.str(); }
    operator HtmlElement() { return m_root; }
};

int main() {
    auto builder = HtmlElement::build("ul");
    builder->add_child("li", "hello")->add_child("li", "world");

    cout << builder->str() << endl;
    return EXIT_SUCCESS;
}
/*
<ul>
    <li>
        hello
    </li>
    <li>
        world
    </li>
</ul>
*/

Sophisticated & Fluent Builder Design Pattern Example

Person.h
#pragma once
#include <iostream>
using namespace std;

class PersonBuilder;

class Person
{
    std::string m_name, m_street_address, m_post_code, m_city;  // Personal Detail
    std::string m_company_name, m_position, m_annual_income;    // Employment Detail

    Person(std::string name) : m_name(name) {}

public:
    friend class PersonBuilder;
    friend ostream& operator<<(ostream&  os, const Person& obj);
    static PersonBuilder create(std::string name);
};
Person.cpp
#include <iostream>
#include "Person.h"
#include "PersonBuilder.h"

PersonBuilder Person::create(string name) { return PersonBuilder{name}; }

ostream& operator<<(ostream& os, const Person& obj)
{
    return os << obj.m_name
              << std::endl
              << "lives : " << std::endl
              << "at " << obj.m_street_address
              << " with postcode " << obj.m_post_code
              << " in " << obj.m_city
              << std::endl
              << "works : " << std::endl
              << "with " << obj.m_company_name
              << " as a " << obj.m_position
              << " earning " << obj.m_annual_income;
}
PersonBuilder.h
#pragma once
#include "Person.h"

class PersonBuilder
{
    Person person;

public:
    PersonBuilder(string name) : person(name) {}

    operator Person() const { return move(person); }

    PersonBuilder&  lives();
    PersonBuilder&  at(std::string street_address);
    PersonBuilder&  with_postcode(std::string post_code);
    PersonBuilder&  in(std::string city);
    PersonBuilder&  works();
    PersonBuilder&  with(string company_name);
    PersonBuilder&  as_a(string position);
    PersonBuilder&  earning(string annual_income);
};
PersonBuilder.cpp
#include "PersonBuilder.h"

PersonBuilder&  PersonBuilder::lives() { return *this; }

PersonBuilder&  PersonBuilder::works() { return *this; }

PersonBuilder&  PersonBuilder::with(string company_name) {
    person.m_company_name = company_name; 
    return *this;
}

PersonBuilder&  PersonBuilder::as_a(string position) {
    person.m_position = position; 
    return *this;
}

PersonBuilder&  PersonBuilder::earning(string annual_income) {
    person.m_annual_income = annual_income; 
    return *this;
}

PersonBuilder&  PersonBuilder::at(std::string street_address) {
    person.m_street_address = street_address; 
    return *this;
}

PersonBuilder&  PersonBuilder::with_postcode(std::string post_code) {
    person.m_post_code = post_code; 
    return *this;
}

PersonBuilder&  PersonBuilder::in(std::string city) {
    person.m_city = city; 
    return *this;
}
Main.cpp
#include <iostream>
#include "Person.h"
#include "PersonBuilder.h"
using namespace std;

int main()
{
    Person p = Person::create("John")
                                .lives()
                                    .at("123 London Road")
                                    .with_postcode("SW1 1GB")
                                    .in("London")
                                .works()
                                    .with("PragmaSoft")
                                    .as_a("Consultant")
                                    .earning("10e6");

    cout << p << endl;
    return EXIT_SUCCESS;
}

Benefits of Builder Design Pattern

Summary by FAQs

When should the Builder Design Pattern be used?
Whenever creation of new object requires setting many parameters and some of them (or all of them) are optional.
Why do we need a Builder class when implementing a Builder Design Pattern?
It isn’t necessary but there are some benefits in doing so:
Greatest Advantage of Builder Design Pattern!
More expressive code.
MyClass o = new MyClass(5, 5.5, 'A', var, 1000, obj9, "hello");
What’s the difference between Abstract Factory and Builder Design Pattern?