Object-Oriented Programming (C++)

Chapter 3
Classes, Encapsulation and Interfaces

Bachelors Program in Computer Science Engineering
Object-Oriented Programming (C++)   •   Bachelor

Classes, Encapsulation and Interfaces

Introduction

A class defines a new type by combining state with operations that control that state. Effective object-oriented design does more than place variables and functions inside a class: it establishes valid states, restricts unauthorized changes, exposes a stable interface, and hides implementation decisions.

Encapsulation is therefore not merely the use of private. It is the design discipline of ensuring that objects can be manipulated only through operations that preserve their invariants. This discipline improves correctness, security, testability and maintainability.

In C++, class design also requires attention to constructors, copying and moving, static members, friend declarations, exception safety and the separation of interfaces from implementations. These mechanisms influence whether a class remains correct under realistic conditions such as arithmetic overflow, concurrency and object copying.

Learning Objectives

After completing this chapter, students should be able to:

  • define classes with appropriate public, protected and private members;
  • formulate and preserve class invariants;
  • design constructors that establish valid objects;
  • implement const-correct member functions;
  • use static data and function members safely;
  • control copy and move operations;
  • apply friend declarations selectively;
  • separate class interfaces from implementations;
  • apply information hiding, cohesion, coupling and design-by-contract principles;
  • recognize overflow, concurrency and lifetime hazards in class designs.

1. Classes as User-Defined Types

A class declaration describes the data representation and operations of a user-defined type.

class Temperature {
public:
    explicit Temperature(double celsius);

    [[nodiscard]] double celsius() const noexcept;
    [[nodiscard]] double fahrenheit() const noexcept;

private:
    double celsius_;
};

An object is an instance of a class. Each Temperature object has its own celsius_ value, while all objects share the member-function definitions.

The declaration has two important roles:

  1. It tells clients which operations are available.
  2. It defines which implementation details clients may not access directly.

The public part forms the class interface. The private part represents hidden state and supporting implementation details.

1.1 class and struct

In C++, both class and struct can contain data, functions, constructors and access specifiers. Their primary language-level difference is the default access:

DeclarationDefault member accessDefault inheritance access
classprivateprivate
structpublicpublic

A common convention is to use struct for simple aggregate-like records and class for types that actively protect invariants. This is a convention rather than a language restriction.

2. Encapsulation, Information Hiding and Invariants

2.1 Encapsulation and information hiding

Encapsulation groups related state and behavior into one abstraction. Information hiding conceals design decisions that clients should not depend upon. The concepts are related but not identical.

For example, a bank account could expose its balance publicly:

struct UnsafeAccount {
    std::int64_t balance_cents;
};

Any client could assign -5000, bypassing the rule that the balance must remain nonnegative. Grouping the balance in a structure has occurred, but effective information hiding has not.

A protected design makes the balance private and allows changes only through validated operations.

2.2 Class invariants

A class invariant is a condition that must hold for every publicly observable, fully constructed object. Consider an account storing an owner and a balance measured in cents:

I(a) ≡ (a.balance ≥ 0) ∧ (a.owner ≠ empty)

Here, I(a) denotes the invariant of account a. The balance is represented in cents, so its unit is an integer number of cents rather than a floating-point currency value.

Public operations may temporarily manipulate internal values, but the invariant must hold when control returns to the client. Constructors establish it, member functions preserve it, and destructors safely end the object's lifetime.

2.3 Why data members are usually private

Private data provides several benefits:

  • validation can be centralized;
  • representation can change without forcing client changes;
  • debugging is easier because mutations pass through known operations;
  • security-sensitive state is less exposed;
  • invalid intermediate states are less likely to escape.

Providing a setter for every data member does not automatically create good encapsulation. An operation such as withdraw(500) expresses domain intent more clearly than setBalance(balance() - 500) and can enforce domain rules centrally.

Class interface protecting private representation Client code deposit() withdraw() balance() Account abstraction Public interface Validates requests Preserves invariant Reports results Private state owner_ balance_ No direct client access
Class interface protecting private representation
Figure: Class interface protecting private representation

Figure: A public interface mediates all access to a class's private representation.

3. Access Control

C++ provides three access levels.

Access levelSame classFriendsDerived classesUnrelated clients
publicYesYesYesYes
protectedYesYesYesNo
privateYesYesNoNo

Public members constitute client-visible services. Private members support the implementation. Protected members are accessible in derived classes, but excessive protected data weakens encapsulation because every derived class becomes dependent on the representation.

Access control is checked by the compiler. It is not, by itself, an encryption or hostile-process security mechanism. Memory corruption, unsafe casts or privileged debugging tools may still expose data.

4. Constructors and Object Initialization

A constructor initializes an object and establishes its invariant. It has the class name and no return type.

Temperature::Temperature(double celsius)
    : celsius_{celsius} {
    if (celsius < -273.15) {
        throw std::invalid_argument{"temperature below absolute zero"};
    }
}

4.1 Member-initializer lists

Data members are initialized before the constructor body executes. A member-initializer list constructs them directly and is required for references, const data members and base classes.

Members are initialized in their declaration order, not in the textual order of the initializer list. Initializer lists should therefore follow declaration order to avoid misleading code.

4.2 Default and converting constructors

A constructor callable with no arguments is a default constructor. A constructor callable with one argument may define an implicit conversion unless marked explicit.

class EmployeeId {
public:
    explicit EmployeeId(int value);
private:
    int value_;
};

explicit prevents an integer from silently becoming an EmployeeId, reducing accidental type conversions.

4.3 Delegating constructors

One constructor may delegate to another constructor of the same class:

class Point {
public:
    Point() : Point{0, 0} {}
    Point(int x, int y) : x_{x}, y_{y} {}
private:
    int x_;
    int y_;
};

Delegation avoids duplicating initialization logic.

4.4 Constructor failure

If a constructor throws an exception, the object is not considered fully constructed, and its destructor is not called. However, already constructed members and base subobjects are destroyed automatically. A constructor should validate inputs before performing externally visible side effects whenever practical.

5. Member Functions and Const-Correctness

A non-static member function receives an implicit pointer named this, which points to the object on which the function was invoked.

void Counter::increment() {
    ++this->value_;
}

The explicit this-> is usually optional.

A const member function promises not to modify the object's logical state through that interface:

[[nodiscard]] std::int64_t balance() const noexcept;

Such a function can be called on const and non-const objects. A non-const member function cannot normally be called on a const object.

noexcept states that the function does not allow exceptions to escape. It should be used only when the implementation can satisfy that guarantee. [[nodiscard]] asks the compiler to warn when a meaningful return value is ignored.

A mutable data member may be changed by a const member function. Appropriate uses include caches and synchronization primitives that do not change logical state. Using mutable to bypass sound const design is poor practice.

6. A Robust Account Class

The following class stores monetary values as signed 64-bit integer cents. Integer cents avoid binary floating-point rounding for currencies with two-decimal-place accounting in this simplified model. Real financial systems must additionally define currency, rounding, taxation and regulatory rules.

6.1 Interface

// Account.hpp
#ifndef ACCOUNT_HPP
#define ACCOUNT_HPP

#include <cstdint>
#include <string>

class Account {
public:
    using Money = std::int64_t; // cents

    explicit Account(std::string owner, Money opening_balance = 0);

    [[nodiscard]] const std::string& owner() const noexcept;
    [[nodiscard]] Money balance() const noexcept;

    void deposit(Money amount);
    void withdraw(Money amount);
    void transfer_to(Account& target, Money amount);

private:
    std::string owner_;
    Money balance_;
};

#endif

6.2 Implementation

// Account.cpp
#include "Account.hpp"

#include <limits>
#include <stdexcept>
#include <utility>

Account::Account(std::string owner, Money opening_balance)
    : owner_{std::move(owner)}, balance_{opening_balance} {
    if (owner_.empty()) {
        throw std::invalid_argument{"owner must not be empty"};
    }
    if (balance_ < 0) {
        throw std::invalid_argument{"opening balance must be nonnegative"};
    }
}

const std::string& Account::owner() const noexcept {
    return owner_;
}

Account::Money Account::balance() const noexcept {
    return balance_;
}

void Account::deposit(Money amount) {
    if (amount <= 0) {
        throw std::invalid_argument{"deposit must be positive"};
    }

    const Money maximum = std::numeric_limits<Money>::max();
    if (balance_ > maximum - amount) {
        throw std::overflow_error{"balance would overflow"};
    }

    balance_ += amount;
}

void Account::withdraw(Money amount) {
    if (amount <= 0) {
        throw std::invalid_argument{"withdrawal must be positive"};
    }
    if (amount > balance_) {
        throw std::domain_error{"insufficient funds"};
    }

    balance_ -= amount;
}

void Account::transfer_to(Account& target, Money amount) {
    if (&target == this) {
        throw std::invalid_argument{"source and target must differ"};
    }
    if (amount <= 0) {
        throw std::invalid_argument{"transfer must be positive"};
    }
    if (amount > balance_) {
        throw std::domain_error{"insufficient funds"};
    }

    const Money maximum = std::numeric_limits<Money>::max();
    if (target.balance_ > maximum - amount) {
        throw std::overflow_error{"target balance would overflow"};
    }

    balance_ -= amount;
    target.balance_ += amount;
}

6.3 Preventing signed-integer overflow

Signed-integer overflow is undefined behavior in C++. Code must check whether addition is representable before performing it. Because deposit first establishes amount > 0, maximum - amount is itself representable. The safe-addition condition is:

balance ≤ MAX − amount

Here, balance and amount are measured in cents, and MAX is the maximum value of Account::Money. The expression is applicable because both the existing balance and amount are nonnegative at the check.

For example, the maximum signed 64-bit value is 9,223,372,036,854,775,807. If the balance is 9,223,372,036,854,775,767 cents, depositing 50 cents would exceed the maximum by 10 cents. The operation therefore throws before addition occurs, leaving the original balance unchanged.

Merely changing the type from int to std::int64_t does not eliminate overflow; it only increases the range. The explicit boundary check is still necessary.

6.4 Preserving transfer consistency

transfer_to validates every condition before changing either account. Once validation succeeds, both arithmetic operations are known to be representable and cannot throw. Thus, either no balance changes, or both changes complete.

If the source balance is 12,500 cents and the target balance is 3,000 cents, transferring 2,750 cents produces:

  • source: 12,500 − 2,750 = 9,750 cents;
  • target: 3,000 + 2,750 = 5,750 cents;
  • total before and after: 15,500 cents.

This conserves the amount represented by the two objects. It does not create a durable database transaction and is not thread-safe without external synchronization.

7. Design by Contract

Design by contract describes an operation using preconditions, postconditions and invariants.

  • A precondition must be satisfied by the caller.
  • A postcondition must hold after successful completion.
  • An invariant must hold for all publicly observable valid objects.

For a successful deposit, let b be the old balance, b′ the new balance, x the deposit amount and MAX the largest representable balance:

Pre: x > 0 ∧ b ≤ MAX − x
Post: b′ = b + x

If a public API accepts untrusted input, checking preconditions at runtime is generally safer than relying only on documentation. Exceptions, error codes or result types may report violations; the choice should be consistent across the interface.

The account operations provide the strong exception guarantee: if validation fails and an exception is thrown, the observable account state remains unchanged.

Assertions such as assert(balance_ >= 0) are useful for detecting programmer errors during development. They should not be the only validation for external input because assertions may be disabled in release builds.

8. Static Members and Object Lifetimes

A static data member belongs to the class rather than to each object. A static member function has no this pointer and can directly access only static members.

A common application is counting live instances. Copy and move semantics must be handled explicitly: if an implicitly copied object does not increment the count but its destructor decrements it, the counter becomes incorrect.

#include <atomic>
#include <cstddef>

class Session final {
public:
    Session() noexcept {
        active_.fetch_add(1, std::memory_order_relaxed);
    }

    ~Session() {
        active_.fetch_sub(1, std::memory_order_relaxed);
    }

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

    [[nodiscard]] static std::size_t active() noexcept {
        return active_.load(std::memory_order_relaxed);
    }

private:
    inline static std::atomic_size_t active_{0};
};

Copy and move construction are deleted, so every live Session must have executed the counting constructor exactly once and will execute one matching destructor. Assignment is also deleted because a session represents a distinct active lifetime rather than a transferable value.

The atomic counter prevents a data race when sessions are created and destroyed by multiple threads. Relaxed ordering is sufficient for an independent count because the counter is not being used to publish or synchronize other data. A returned count is a valid snapshot, but it may immediately become outdated as other threads create or destroy sessions.

An alternative design could implement copy or move operations explicitly. A copy constructor would need to increment the count. A move design would require a clearly specified ownership model and a moved-from state. Deleting unsupported operations is often safer than accepting incorrect compiler-generated behavior.

9. Copy and Move Control

C++ may generate a destructor, copy constructor, copy assignment operator, move constructor and move assignment operator. These are often called special member functions.

Compiler-generated operations are suitable when each data member already has correct value semantics. This is the rule of zero: prefer members such as std::string, std::vector and smart pointers so that the class does not manually manage resources.

If a class directly manages a resource, special operations require deliberate design. Common choices are:

  • support value-like copying by implementing correct deep copies;
  • support unique ownership by deleting copy operations and enabling moves;
  • prohibit both copying and moving when object identity must remain fixed.

Deleting an operation communicates the restriction at compile time:

Session(const Session&) = delete;

10. Friend Declarations

A class may grant a function or another class access to its private and protected members.

#include <ostream>

class Coordinate {
public:
    Coordinate(int x, int y) : x_{x}, y_{y} {}

    friend std::ostream& operator<<(std::ostream&, const Coordinate&);

private:
    int x_;
    int y_;
};

The output operator can access x_ and y_, although it is not a member function. Friendship is neither inherited, transitive nor automatically reciprocal.

Friendship should be narrow and intentional. It is appropriate when an operation is conceptually part of an abstraction but is better represented as a non-member, as with symmetric operators. Broad friendship increases coupling and weakens the class boundary.

Objects of the same class may access one another's private members without friendship. This is why Account::transfer_to can access target.balance_.

11. Separating Interface and Implementation

Traditional C++ projects place class declarations in header files and non-inline definitions in source files.

11.1 Header responsibilities

A header normally contains:

  • the class declaration;
  • public types and function signatures;
  • private data required to determine object layout;
  • necessary declarations or included dependencies;
  • include guards or #pragma once.

11.2 Source-file responsibilities

A source file normally contains:

  • non-inline member-function definitions;
  • private algorithms not required by clients;
  • implementation-only dependencies.

This separation improves readability and reduces unnecessary recompilation. It also allows implementation changes that do not alter the public interface or object layout.

However, private data in a conventional declaration can still create build and binary dependencies because clients need the object size and layout. The Pointer to Implementation, or Pimpl, idiom hides representation behind a private pointer. Pimpl can improve binary-interface stability, but it introduces allocation, indirection and implementation complexity.

C++20 modules offer a modern alternative for organizing exported interfaces while reducing textual inclusion. Modules improve dependency management, although they do not eliminate the need for careful abstraction and compatibility design.

12. Cohesion and Coupling

Cohesion measures how strongly the responsibilities within a module or class belong together. Coupling measures how strongly one module depends on another.

A well-designed class generally has high cohesion and low coupling.

Design observationLikely consequence
One class handles accounts, email and user-interface renderingLow cohesion
Clients directly modify private representation through exposed referencesTight coupling
Operations use domain concepts such as withdrawStronger cohesion
Interfaces depend on abstractions rather than concrete servicesLower coupling
Broad friend access across many classesIncreased coupling

A class should have a focused reason to change. Excessively large “god classes” become difficult to test and maintain. Conversely, dividing behavior into many trivial classes can create needless navigation and interaction complexity. Good design balances responsibility boundaries against practical simplicity.

13. Illustrative Case: A Digital Wallet Transfer Service

Background

This fictional case concerns a campus digital-wallet application. Students use wallet balances, represented in cents, to pay for meals and printing.

Problem

An early implementation exposed balances publicly. Application code subtracted from one wallet and later added to another. Defects included negative balances, partially completed transfers and overflow when a corrupted destination balance approached the integer limit.

Analysis

The public balance allowed every client to become responsible for validation. Consequently, rules were duplicated and inconsistently enforced. The two transfer updates could also be separated by an exception, producing lost value.

The required invariants were:

  • each wallet had a nonempty owner identifier;
  • each balance was nonnegative;
  • successful transfers conserved the combined balance;
  • every stored balance remained representable by its integer type.

Approach and Solution

The development team replaced public balances with an Account-like class. Construction validated the owner and opening balance. Deposits and withdrawals checked their contracts. Transfer validation occurred before mutation, including the destination overflow check.

The public header exposed only domain operations. Storage details and validation logic remained in the source file. Unit tests covered zero and negative amounts, insufficient funds, self-transfer, maximum representable balances and successful transfers.

For the production system, database transactions and row-level concurrency controls were also required. An in-memory C++ class can preserve local invariants, but it cannot alone ensure durability or consistency across processes.

Outcome

Invalid balance assignments became impossible through the ordinary public API. Failed transfers left both wallet objects unchanged, and overflow attempts were rejected before unsafe arithmetic occurred. Centralized rules also simplified auditing and testing.

Learning Points

  • Private state is valuable only when public operations preserve meaningful invariants.
  • Overflow checks are part of correctness, not optional optimization.
  • Multi-object operations should validate all failure conditions before mutation when feasible.
  • Local object correctness does not replace persistent transaction management.
  • Interface design affects security, testing and operational governance.

14. Testing Class Contracts

Tests should examine normal behavior, boundary conditions and invalid inputs. For Account, useful tests include:

  • construction with valid and invalid owners;
  • zero, negative and positive deposits;
  • deposits near the maximum representable value;
  • withdrawals equal to, below and above the balance;
  • self-transfer and insufficient-funds transfer;
  • conservation of total balance after successful transfer;
  • unchanged state after exceptions.

Property-based testing can generate many input combinations and verify properties such as nonnegative balances and transfer conservation. Static analysis and sanitizers can detect additional problems, although overflow must still be prevented by correct program logic.

15. Professional, Ethical and Governance Considerations

Class interfaces often enforce rules with legal, financial or safety consequences. Developers should document units, limits, rounding policies, error behavior and thread-safety guarantees. Misleading names or undocumented conversions can cause material harm.

Financial and personal data should be exposed only when required. A getter returning sensitive information may preserve language-level encapsulation while violating privacy principles. Audit logs should avoid leaking credentials or unnecessary personal data.

AI-assisted tools can propose class designs and generate tests, but generated code must be reviewed for incorrect lifetime assumptions, arithmetic overflow, accidental copying, race conditions and overly broad access. Responsibility for deployed behavior remains with the engineering organization.

16. Design Guidelines and Limitations

Effective class design commonly follows these practices:

  1. Define the invariant before writing public operations.
  2. Keep data private unless the type is intentionally a transparent record.
  3. Use constructors to establish valid state.
  4. Prefer domain operations over unrestricted setters.
  5. Mark observers const and use noexcept only when justified.
  6. Use explicit to prevent unintended single-argument conversions.
  7. Check arithmetic before performing potentially overflowing operations.
  8. Prefer the rule of zero; otherwise specify copy and move behavior deliberately.
  9. Minimize friendship and exposed implementation dependencies.
  10. Test boundaries and exception behavior, not only typical cases.

Encapsulation has limits. It cannot by itself prevent memory corruption, ensure database durability, provide network authorization or make an unsynchronized object thread-safe. These concerns require additional system-level mechanisms.

Chapter Summary

A C++ class is a user-defined type whose public interface controls access to its representation. Encapsulation combines state and behavior, while information hiding prevents clients from depending on implementation decisions. Constructors establish invariants, and member functions must preserve them.

Access control, const-correctness, explicit constructors and carefully designed domain operations support reliable interfaces. Arithmetic must be checked before an operation that could overflow. Static members represent class-wide state, but correct instance counting requires explicit treatment of copy and move construction. Friend declarations offer selective access but increase coupling.

Separating declarations from definitions improves modularity and build organization. High cohesion, low coupling, design by contract, exception safety and deliberate special-member-function policies produce classes that are easier to test and maintain. These local design principles must be combined with system-level concurrency, persistence, security and governance controls in professional software.

Key Terms

Abstraction: A simplified model exposing essential behavior while suppressing unnecessary detail.

Access control: Language rules determining where class members may be used.

Class invariant: A condition that holds for every publicly observable valid object.

Cohesion: The degree to which responsibilities within a module belong together.

Const-correctness: Consistent use of const to distinguish modifying and non-modifying access.

Constructor: A special member function that initializes an object.

Coupling: The degree of dependency between software components.

Design by contract: Specification of operations through preconditions, postconditions and invariants.

Encapsulation: Combining state and operations within a controlled abstraction.

Exception guarantee: A stated condition concerning program state when an operation throws.

Friend: A function or class granted access to another class's non-public members.

Information hiding: Concealing implementation decisions that clients should not depend upon.

Interface: The publicly available operations and types through which clients use an abstraction.

Member-initializer list: Constructor syntax that directly initializes bases and data members.

Postcondition: A condition guaranteed after successful completion of an operation.

Precondition: A condition required before an operation is invoked.

Rule of zero: The guideline that resource management should be delegated so custom special member functions are unnecessary.

Static member: A member associated with the class as a whole rather than each object.

Review Questions

Short Answer Questions

  1. Distinguish encapsulation from information hiding.
  2. What is a class invariant, and when must it hold?
  3. Why should a single-argument constructor often be marked explicit?
  4. Why does a static member function have no this pointer?
  5. Why must signed addition be checked before it is performed?

Long Answer Questions

  1. Explain how constructors, access control and public member functions cooperate to preserve a class invariant.
  2. Compare compiler-generated copy and move operations with explicitly deleted or implemented operations.
  3. Discuss the benefits and costs of separating a C++ class interface from its implementation.
  4. Analyze the relationship among cohesion, coupling, friendship and maintainability.
  5. Explain design by contract and exception safety using Account::deposit and Account::transfer_to as examples.

Analytical / Application Questions

  1. Design a BoundedCounter class whose value must remain between specified minimum and maximum limits. State its invariant and describe how overflow and invalid construction will be handled.
  2. An instance counter increments only in a default constructor and decrements in the destructor. Analyze what happens when an object is copied, and propose two correct redesigns.
  3. Evaluate a wallet API that exposes std::int64_t& balance(). Identify the invariant, security and coupling risks, then propose a safer interface.

Multiple Choice Questions with Answers

Question 1

Which statement best defines a class invariant?

A. A condition that must hold only before construction

B. A condition that must hold for every publicly observable valid object

C. A comment describing private data

D. A rule applied only to static members

Correct Answer: B. A condition that must hold for every publicly observable valid object

Question 2

Why does Account::deposit check balance_ > maximum - amount before addition?

A. To prevent signed-integer overflow before it occurs

B. To convert the balance to floating point

C. To ensure the amount is negative

D. To make the function thread-safe

Correct Answer: A. To prevent signed-integer overflow before it occurs

Question 3

What is the effect of marking a single-argument constructor explicit?

A. It makes the constructor private

B. It deletes the default constructor

C. It prevents unintended implicit conversions through that constructor

D. It guarantees that construction cannot throw

Correct Answer: C. It prevents unintended implicit conversions through that constructor

Question 4

Why are copying and moving deleted in the chapter's Session class?

A. Atomic variables cannot appear in classes

B. Every session must have a distinct counted lifetime

C. Destructors cannot access static members

D. Static functions require immovable objects

Correct Answer: B. Every session must have a distinct counted lifetime

Question 5

What would be wrong with an active-object counter that increments only in the ordinary constructor but permits implicit copying?

A. Copied objects would increment the counter twice

B. Copied objects would never be destroyed

C. Copied objects would later decrement a count they never incremented

D. The counter would become a non-static member

Correct Answer: C. Copied objects would later decrement a count they never incremented

Question 6

Which member function declaration is an observer callable on a const object?

A. Money balance() const noexcept;

B. void deposit(Money amount);

C. Money& balance();

D. static void withdraw(Money amount);

Correct Answer: A. Money balance() const noexcept;

Question 7

Which statement about C++ friendship is correct?

A. Friendship is automatically reciprocal

B. Friendship is inherited by derived classes

C. Friendship is transitive between classes

D. Friendship grants specified access but is neither reciprocal, inherited nor transitive

Correct Answer: D. Friendship grants specified access but is neither reciprocal, inherited nor transitive

Question 8

A source account contains 12,500 cents and transfers 2,750 cents to an account containing 3,000 cents. What is the destination balance after a successful transfer?

A. 250 cents

B. 5,750 cents

C. 9,750 cents

D. 15,500 cents

Correct Answer: B. 5,750 cents

Question 9

Which design most directly supports high cohesion?

A. A class responsible for accounts, email delivery and screen rendering

B. A class exposing references to all private members

C. A class focused on account operations such as deposits and withdrawals

D. A class declaring every other class as a friend

Correct Answer: C. A class focused on account operations such as deposits and withdrawals

Question 10

What does the strong exception guarantee mean for a failed deposit operation?

A. The program must terminate immediately

B. The balance remains observably unchanged

C. The exception is silently ignored

D. The balance is reset to zero

Correct Answer: B. The balance remains observably unchanged

Object-Oriented Programming (C++)
Chapter 3 of 15