Coming Soon

This lesson is currently being developed

Default member initialization

Set default values for struct members.

Compound Types: Enums and Structs
Chapter
Beginner
Difficulty
35min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

13.9 — Default member initialization

In this lesson, you'll learn about default member initialization - a convenient way to provide default values for struct members right in the struct definition, ensuring they're always initialized to meaningful values.

The problem with uninitialized members

In previous lessons, you learned that uninitialized struct members contain garbage values:

#include <iostream>

struct GameStats
{
    int score;
    int lives;
    int level;
};

int main()
{
    GameStats player;  // Members are uninitialized!
    
    std::cout << "Score: " << player.score << std::endl;  // Garbage value
    std::cout << "Lives: " << player.lives << std::endl;  // Garbage value
    std::cout << "Level: " << player.level << std::endl;  // Garbage value
    
    return 0;
}

This can lead to bugs because your program starts with unpredictable values. While aggregate initialization helps, what if you forget to initialize a struct or only partially initialize it?

Introduction to default member initialization

Default member initialization allows you to provide default values for struct members directly in the struct definition. These default values are used whenever a member isn't explicitly initialized:

#include <iostream>

struct GameStats
{
    int score{0};    // Default initialize to 0
    int lives{3};    // Default initialize to 3
    int level{1};    // Default initialize to 1
};

int main()
{
    GameStats player;  // Members get their default values automatically
    
    std::cout << "Score: " << player.score << std::endl;  // 0
    std::cout << "Lives: " << player.lives << std::endl;  // 3
    std::cout << "Level: " << player.level << std::endl;  // 1
    
    return 0;
}

Output:

Score: 0
Lives: 3
Level: 1

Now your struct members always have meaningful values!

Syntax for default member initialization

You can use several different syntax forms for default member initialization:

#include <iostream>
#include <string>

struct Product
{
    int id{0};                          // Brace initialization (preferred)
    std::string name = "Unknown";       // Copy initialization with =
    double price{0.0};                  // Brace initialization
    bool inStock = true;                // Copy initialization with =
};

int main()
{
    Product item;  // All members get their default values
    
    std::cout << "ID: " << item.id << std::endl;
    std::cout << "Name: " << item.name << std::endl;
    std::cout << "Price: $" << item.price << std::endl;
    std::cout << "In Stock: " << (item.inStock ? "Yes" : "No") << std::endl;
    
    return 0;
}

Output:

ID: 0
Name: Unknown
Price: $0
In Stock: Yes

Default values work with aggregate initialization

When you use aggregate initialization, any members you don't specify will use their default values:

#include <iostream>
#include <string>

struct Rectangle
{
    double width{1.0};      // Default width
    double height{1.0};     // Default height
    std::string color{"white"};  // Default color
};

int main()
{
    Rectangle rect1;                    // All defaults: 1x1 white
    Rectangle rect2{5.0};               // 5x1 white (height and color are default)
    Rectangle rect3{3.0, 4.0};         // 3x4 white (color is default)
    Rectangle rect4{2.0, 2.0, "red"};  // 2x2 red (no defaults used)
    
    std::cout << "Rectangle 1: " << rect1.width << "x" << rect1.height 
              << " (" << rect1.color << ")" << std::endl;
    std::cout << "Rectangle 2: " << rect2.width << "x" << rect2.height 
              << " (" << rect2.color << ")" << std::endl;
    std::cout << "Rectangle 3: " << rect3.width << "x" << rect3.height 
              << " (" << rect3.color << ")" << std::endl;
    std::cout << "Rectangle 4: " << rect4.width << "x" << rect4.height 
              << " (" << rect4.color << ")" << std::endl;
    
    return 0;
}

Output:

Rectangle 1: 1x1 (white)
Rectangle 2: 5x1 (white)
Rectangle 3: 3x4 (white)
Rectangle 4: 2x2 (red)

Real-world example: Configuration settings

Default member initialization is especially useful for configuration-style structs:

#include <iostream>
#include <string>

struct DatabaseConfig
{
    std::string host{"localhost"};
    int port{5432};
    std::string database{"myapp"};
    std::string username{"user"};
    bool enableSSL{true};
    int maxConnections{100};
    int timeoutSeconds{30};
};

void printConfig(const DatabaseConfig& config)
{
    std::cout << "Database Configuration:" << std::endl;
    std::cout << "  Host: " << config.host << std::endl;
    std::cout << "  Port: " << config.port << std::endl;
    std::cout << "  Database: " << config.database << std::endl;
    std::cout << "  Username: " << config.username << std::endl;
    std::cout << "  SSL: " << (config.enableSSL ? "Enabled" : "Disabled") << std::endl;
    std::cout << "  Max Connections: " << config.maxConnections << std::endl;
    std::cout << "  Timeout: " << config.timeoutSeconds << " seconds" << std::endl;
}

int main()
{
    // Use all defaults
    DatabaseConfig defaultConfig;
    std::cout << "Default configuration:" << std::endl;
    printConfig(defaultConfig);
    
    std::cout << std::endl;
    
    // Override just a few settings
    DatabaseConfig productionConfig{
        "prod-server.company.com",  // host
        3306,                       // port
        "production_db"             // database
        // username, enableSSL, maxConnections, timeoutSeconds use defaults
    };
    std::cout << "Production configuration:" << std::endl;
    printConfig(productionConfig);
    
    return 0;
}

Output:

Default configuration:
Database Configuration:
  Host: localhost
  Port: 5432
  Database: myapp
  Username: user
  SSL: Enabled
  Max Connections: 100
  Timeout: 30 seconds

Production configuration:
Database Configuration:
  Host: prod-server.company.com
  Port: 3306
  Database: production_db
  Username: user
  SSL: Enabled
  Max Connections: 100
  Timeout: 30 seconds

Mixing different types of defaults

You can use different default initialization approaches for different members:

#include <iostream>
#include <string>
#include <vector>

struct Student
{
    int studentId{0};
    std::string name = "New Student";
    double gpa{0.0};
    std::vector<std::string> courses{};  // Empty vector by default
    bool isActive{true};
};

int main()
{
    Student alice{12345, "Alice Johnson"};  // Override ID and name, rest are defaults
    
    std::cout << "Student ID: " << alice.studentId << std::endl;
    std::cout << "Name: " << alice.name << std::endl;
    std::cout << "GPA: " << alice.gpa << std::endl;
    std::cout << "Courses enrolled: " << alice.courses.size() << std::endl;
    std::cout << "Active: " << (alice.isActive ? "Yes" : "No") << std::endl;
    
    return 0;
}

Output:

Student ID: 12345
Name: Alice Johnson
GPA: 0
Courses enrolled: 0
Active: Yes

Default initialization with complex types

Default member initialization works well with more complex types like strings, containers, and nested structs:

#include <iostream>
#include <string>
#include <vector>

struct Address
{
    std::string street = "123 Main St";
    std::string city = "Springfield";
    std::string state = "IL";
    int zipCode{62701};
};

struct Person
{
    std::string name = "Unknown Person";
    int age{0};
    Address address{};  // Use Address struct's defaults
    std::vector<std::string> hobbies{"reading"};  // Default to one hobby
};

int main()
{
    Person defaultPerson;  // All defaults
    
    std::cout << "Name: " << defaultPerson.name << std::endl;
    std::cout << "Age: " << defaultPerson.age << std::endl;
    std::cout << "Address: " << defaultPerson.address.street << ", " 
              << defaultPerson.address.city << ", " << defaultPerson.address.state 
              << " " << defaultPerson.address.zipCode << std::endl;
    std::cout << "Hobbies: ";
    for (const auto& hobby : defaultPerson.hobbies)
    {
        std::cout << hobby << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Name: Unknown Person
Age: 0
Address: 123 Main St, Springfield, IL 62701
Hobbies: reading 

Common use cases for default member initialization

1. Safety values for critical members

struct SafetySystem
{
    bool emergencyStop{true};     // Default to safe state
    int maxSpeed{25};             // Conservative default
    bool alarmsEnabled{true};     // Default to enabled
};

2. Reasonable defaults for optional parameters

struct WindowSettings
{
    int width{800};
    int height{600};
    bool resizable{true};
    bool fullscreen{false};
    std::string title{"My Application"};
};

3. Initialize counters and flags

struct Statistics
{
    int totalRequests{0};
    int successfulRequests{0};
    int failedRequests{0};
    bool isRunning{false};
};

Best practices for default member initialization

1. Provide sensible defaults

// Good: meaningful defaults
struct Timer
{
    int seconds{0};          // Start at zero
    bool isRunning{false};   // Not running initially
};

// Avoid: meaningless defaults
struct Temperature
{
    double celsius{999.9};   // 999.9°C doesn't make sense as a default
};

2. Use brace initialization when possible

struct Point
{
    double x{0.0};    // Preferred: brace initialization
    double y = 0.0;   // Also fine: copy initialization
};

3. Be consistent within a struct

// Good: consistent initialization style
struct Rectangle
{
    double width{1.0};
    double height{1.0};
    std::string color{"white"};
};

// Less ideal: mixed styles (but still works)
struct Rectangle
{
    double width{1.0};
    double height = 1.0;
    std::string color{"white"};
};

4. Consider the order of initialization

struct BankAccount
{
    double balance{0.0};      // Initialize balance first
    int accountNumber{0};     // Then account number
    bool isActive{false};     // Then status flags
};

When defaults are not used

Remember that default member initialization only applies when members aren't explicitly initialized:

#include <iostream>

struct Counter
{
    int value{100};  // Default to 100
};

int main()
{
    Counter c1;        // Uses default: value = 100
    Counter c2{};      // Empty aggregate init: value = 100
    Counter c3{42};    // Explicit init: value = 42 (default not used)
    
    std::cout << "c1.value: " << c1.value << std::endl;  // 100
    std::cout << "c2.value: " << c2.value << std::endl;  // 100
    std::cout << "c3.value: " << c3.value << std::endl;  // 42
    
    return 0;
}

Output:

c1.value: 100
c2.value: 100
c3.value: 42

Key concepts to remember

  1. Default member initialization provides fallback values for struct members that aren't explicitly initialized.

  2. Use brace initialization {} or copy initialization = to specify default values.

  3. Defaults are used only when members aren't explicitly initialized during object creation.

  4. Defaults work with aggregate initialization - unspecified members use their defaults.

  5. Defaults make structs safer by ensuring members always have meaningful values.

  6. Both simple and complex types can have default values.

Summary

Default member initialization is a powerful feature that makes structs more robust and easier to use. By providing sensible default values in the struct definition, you ensure that objects always start in a valid state, even when partially initialized or not initialized at all. This reduces bugs, makes code more maintainable, and provides a better experience for users of your struct types. The feature works seamlessly with aggregate initialization, allowing you to override only the values you need to change while keeping reasonable defaults for everything else.

Quiz

  1. What is default member initialization and when are default values used?
  2. What are the two main syntax forms for specifying default member values?
  3. How does default member initialization interact with aggregate initialization?
  4. Why is it important to choose sensible default values for struct members?
  5. What happens when you explicitly initialize a member that has a default value?

Practice exercises

Try these exercises with default member initialization:

  1. Create a GameSettings struct with defaults for graphics quality, sound volume, and difficulty level. Test creating objects with various levels of initialization.
  2. Create a ServerConfig struct with sensible defaults for a web server (port, max connections, timeout, etc.). Create different configurations by overriding specific values.
  3. Create a StudentRecord struct with defaults and demonstrate how partial aggregate initialization works with the defaults.
  4. Create a nested struct example where both the outer and inner structs have default member initialization.

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion