Skip to content
C++ Better Explained
Go back
Getters and Setters in C++: How and When to Use Them

Getters and Setters in C++

Short answer: a getter reads a private member, a setter writes it. Make getters const.

class Player {
    int health_ = 100;
public:
    int  health() const   { return health_; }   // getter
    void setHealth(int h) { health_ = h; }      // setter
};

The Basic Pattern

#include <iostream>
#include <string>

class Player {
private:
    std::string name_;
    int health_ = 100;

public:
    Player(std::string name) : name_(std::move(name)) {}

    // Getters — const, because they only read
    const std::string& name() const { return name_; }
    int health() const { return health_; }

    // Setters — validate, then assign
    void setHealth(int h) {
        if (h < 0)   h = 0;
        if (h > 100) h = 100;
        health_ = h;
    }
};

int main() {
    Player p("Ana");
    p.setHealth(150);
    std::cout << p.name() << ": " << p.health() << '\n';   // Ana: 100
}

That clamp in setHealth is the entire argument for setters. A public int health; would let any code write p.health = 150 and put the object into a state it should never reach.

Why Getters Must Be const

The const after the parameter list promises the function won’t modify the object:

int health() const { return health_; }
//            ^^^^^

Without it, this breaks:

void printPlayer(const Player& p) {
    std::cout << p.health();   // error if health() is not const
}

Passing large objects by const& is standard practice, so a non-const getter makes your class awkward to use. See the const keyword in C++.

Learning C++ properly? The C++ Better Explained Ebook explains classes, objects and OOP in plain English — 87 pages, just $19.

Return by Value or by Reference?

int health() const { return health_; }                  // small — by value
const std::string& name() const { return name_; }       // large — by const ref

Copying an int is free. Copying a std::string allocates. The rule of thumb:

Why const& and not plain &? A plain reference hands out write access and quietly destroys the encapsulation you built:

std::string& name() { return name_; }   // dangerous
p.name() = "anything";                  // bypasses every check you wrote

Never return a reference to a local:

const std::string& bad() const {
    std::string tmp = name_ + "!";
    return tmp;             // tmp dies here — dangling reference
}

Setters That Actually Earn Their Keep

A setter that just assigns adds nothing over a public member. A setter that enforces a rule does:

class BankAccount {
    double balance_ = 0.0;

public:
    double balance() const { return balance_; }

    bool deposit(double amount) {
        if (amount <= 0) return false;      // reject nonsense
        balance_ += amount;
        return true;
    }

    bool withdraw(double amount) {
        if (amount <= 0 || amount > balance_) return false;
        balance_ -= amount;
        return true;
    }
};

Note there’s no setBalance. The class exposes the operations that make sense rather than raw write access — which is the real point of encapsulation. A setBalance(double) would let any caller invent money.

When to Skip Them Entirely

If a type is just a bundle of values with no rules to enforce, getters and setters are noise:

struct Point {
    double x = 0;
    double y = 0;
};

Point p;
p.x = 3.5;      // clear, and nothing is being protected

Writing getX(), setX(), getY(), setY() around this buys you nothing but typing. Use a struct with public members when there are no invariants.

The test: is there any value this member could hold that would make the object wrong? If yes, you need a setter that guards it. If no, make it public.

Naming Conventions

C++ has no single standard. Common styles:

int  getHealth() const;  void setHealth(int);    // Java-influenced
int  health() const;     void setHealth(int);    // common modern C++
int  health() const;     void health(int);       // overloaded

The middle one is most common in modern C++ and the standard library leans that way (vector::size(), not getSize()). Pick one and hold to it across the codebase.

Quick Reference

SituationDo this
Reading a memberconst getter
Small typeReturn by value
Large typeReturn by const&
Member has validity rulesSetter that validates
No rules to enforcePublic member in a struct
Exposing an operationNamed method, not a raw setter
NeverReturn non-const & to a private member

Take Your C++ Further

If you want classes, objects and OOP explained properly rather than pieced together, the C++ Better Explained Ebook covers it in plain English. Just $19.

👉 Get the C++ Better Explained Ebook — $19


📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Share this post on:

Written by

Sahil Bora

Software Engineer. Author and creator of C++ Better Explained.


Previous Post
How to Break Out of a Nested Loop in C++
Next Post
static_cast vs dynamic_cast in C++: Which One to Use

Keep Learning