C++ PROGRAMMING ยท 18 MIN STUDY

Class design: access control, this, and static members

Design class interfaces using access control, const methods, this, and static members.

Learning objectives

  • Use public, private, and protected access control.
  • Understand the this pointer.
  • Distinguish object members from static class members.
  • Design an interface that preserves class invariants.

Prerequisites: understand classes, objects, member functions, and constructors. Review Classes and objects and Constructors and destructors first.

Language version: the access-control and this examples use longstanding C++ features; the inline static data-member example requires C++17.

Access control

Class members can be public, private, or protected. Public members form the interface available to callers. Private members can be accessed by the class's member functions and friends. Protected members are also accessible to derived classes. Prefer private data and a small public interface so the class can protect its rules.

The this pointer

Inside a non-static member function, this points to the object on which the function was called. It can make member access explicit when a parameter has the same name as a member.

class Counter {
public:
    explicit Counter(int value) : value(value) {}
    void set_value(int value) {
        this->value = value;
    }
private:
    int value;
};

Static data members

A static data member belongs to the class rather than to each individual object. All objects share the same static member. In C++17, an inline static data member can be defined inside the class definition.

C++17 example: an inline static data member can be defined inside the class. This counter deliberately counts calls to the default constructor; it is not a count of every live object or every copy.

class User {
public:
    inline static int default_constructed_count{0};
    User() { ++default_constructed_count; }
};

Static member functions

A static member function can be called using the class name and does not have a this pointer because it is not called on one particular object. It can directly access static members, but not non-static members without an object.

Friend declarations

A friend function or class is granted access to private and protected members. Use friendship sparingly: it creates a close relationship and can weaken encapsulation if overused.

Common mistakes

  • Making data public to avoid writing a proper interface.
  • Trying to access a non-static member directly from a static function.
  • Assuming each object has its own copy of a static data member.
  • Using friend declarations as a shortcut for every helper function.

Practice

  1. Build a BankAccount class with a private balance and public deposit/withdraw functions.
  2. Add a static count of created accounts.
  3. Write a const getter and explain why it is const.
  4. Explain why a static member function cannot use this.

Worked practice solution

This C++17 example stores money as whole paise rather than floating-point rupees. It validates deposits and withdrawals, keeps the balance private, and counts successfully constructed accounts. The counter is cumulative; it does not mean the number of accounts currently alive.

#include <cstddef>
#include <limits>
#include <stdexcept>

class BankAccount {
public:
    explicit BankAccount(long long opening_balance_paise = 0)
        : balance_paise_(opening_balance_paise) {
        if (opening_balance_paise < 0) {
            throw std::invalid_argument("opening balance cannot be negative");
        }
        ++created_count_;
    }

    BankAccount(const BankAccount&) = delete;
    BankAccount& operator=(const BankAccount&) = delete;

    void deposit(long long amount_paise) {
        if (amount_paise <= 0) {
            throw std::invalid_argument("deposit must be positive");
        }
        if (amount_paise > std::numeric_limits<long long>::max() - balance_paise_) {
            throw std::overflow_error("balance would overflow");
        }
        balance_paise_ += amount_paise;
    }

    bool withdraw(long long amount_paise) {
        if (amount_paise <= 0) {
            throw std::invalid_argument("withdrawal must be positive");
        }
        if (amount_paise > balance_paise_) {
            return false; // insufficient funds; balance is unchanged
        }
        balance_paise_ -= amount_paise;
        return true;
    }

    long long balance_paise() const noexcept {
        return balance_paise_;
    }

    static std::size_t accounts_created() noexcept {
        return created_count_;
    }

private:
    long long balance_paise_{0};
    inline static std::size_t created_count_{0};
};
  • balance_paise() is const because reading the balance does not modify the account.
  • accounts_created() is static, so it can be called as BankAccount::accounts_created() without an object and has no this pointer.
  • The copy constructor is deleted so copying an account cannot create an uncounted account object.
  • withdraw returns false for insufficient funds; invalid non-positive amounts are reported with an exception.

The code demonstrates class design, not a complete banking system. A real financial application needs domain-specific policies, persistence, authorization, auditing, and carefully reviewed monetary rules.

Check your understanding

Does a static member function have a this pointer?

Answer: No. It is associated with the class, not a particular object.

Summary

Access control protects class invariants. The this pointer identifies the current object in non-static methods, while static members belong to the class as a whole.

References