CSS Prepare

optional · CSS study notes

5. Member Functions

TypePurposeExample
Accessor ("getter")Reads a data member's value without modifying itdouble getBalance() const { return balance; }
Mutator ("setter")Modifies a data member's value in a controlled wayvoid deposit(double amt) { balance += amt; }
Utility/helperPerforms a computation using the object's datadouble area() { return length * width; }
Constructor/DestructorSpecial member functions for initialization/cleanupsee the Objects and Classes page
  • Member functions defined inside the class body are implicitly inline (compiler may substitute the call with the function body directly, avoiding call overhead)
  • Member functions can be marked const (like getBalance() const) to guarantee they don't modify the object — lets them be called on const objects too

Full worked example — all member function types together

#include <iostream>
using namespace std;

class Student
{
private:
    string name;
    float marks;

public:
    Student(string n, float m) : name(n), marks(m) {}   // constructor

    // Accessor / getter
    float getMarks() const { return marks; }

    // Mutator / setter
    void setMarks(float m) { if (m >= 0 && m <= 100) marks = m; }   // controlled -- validates input

    // Utility / helper -- computes something from the object's own data
    string getGrade() const
    {
        if (marks >= 90) return "A";
        else if (marks >= 80) return "B";
        else return "C";
    }

    // inline by virtue of being defined inside the class body
    void show() const { cout << name << ": " << marks << " (" << getGrade() << ")" << endl; }
};

int main()
{
    Student s("Ali", 85);
    s.show();               // Ali: 85 (B)
    s.setMarks(95);
    s.show();               // Ali: 95 (A)
    return 0;
}

Why mutators validate instead of just assigning

Compare setMarks() above to directly exposing marks as public. The mutator can reject invalid input (e.g., marks = -10 or 150), something a public field can never do — this is the practical reason accessors/mutators exist instead of just making everything public.

const member functions — a common exam trap

class Wallet
{
    double balance;
public:
    double getBalance() const { return balance; }   // promises not to modify the object
    // double getBalance() const { balance += 1; return balance; }  -- ERROR: can't modify in a const function
};

void printBalance(const Wallet& w)
{
    cout << w.getBalance();   // only works because getBalance() is marked const
}

Without const on getBalance(), this function couldn't be called on a const Wallet& parameter at all — a frequent source of confusing compiler errors for students.

Important MCQs

  1. A member function that only reads a data member's value without changing it is called a(n):
    • a) Mutator b) Accessor c) Constructor d) Friend
  2. A member function marked const:
    • a) Cannot be called at all b) Promises not to modify the object's data members c) Can only modify static members d) Is always a constructor
  3. A function defined entirely inside the class body is implicitly treated as:
    • a) private b) virtual c) inline d) static
  4. Why use a mutator ("setter") instead of a public data member directly?
    • a) It's faster b) It allows validation/control over what values are allowed c) It uses less memory d) It's required by C++ syntax

✅ Self-Test Questions (with Answers)

Q1. What's the practical benefit of a mutator function over a public data member?

Ans: A public data member can be assigned any value by any code, and the class has no way to object. A mutator (setter) interposes a function between the outside world and the data, so the class keeps control of every write. The concrete benefits are:

  • Validation — illegal values can be rejected, clamped or reported, keeping the object's invariants true at all times (marks must be 0–100; a radius cannot be negative).
  • Side effects — the setter can recompute a cached value, log the change, mark the object dirty, or notify observers; a bare assignment can do none of this.
  • Implementation freedom — the internal representation can change (store marks as int out of 1000, or compute it from something else) without breaking a single caller, because callers only ever named the function.
  • Read-only or write-only members — provide a getter with no setter, and the member becomes immutable from outside; the reverse gives a write-only field like a password.
  • A single debugging breakpoint — if a member holds a wrong value, one breakpoint in the setter catches every write, instead of hunting through the whole program.
class Student {
    float marks;                       // private: the outside world cannot touch it
public:
    void setMarks(float m) {
        if (m < 0 || m > 100) {        // VALIDATION -- impossible with a public field
            cout << "Invalid marks, ignored\n";
            return;
        }
        marks = m;
    }
    float getMarks() const { return marks; }
};

Student s;
s.setMarks(150);        // prints "Invalid marks, ignored" -- object stays valid
s.setMarks(85);         // accepted
cout << s.getMarks();   // 85

// If 'marks' were public:  s.marks = 150;  would silently corrupt the object.

Exam framing: this is encapsulation delivering its practical payoff. Caution worth adding for extra marks: a setter that merely assigns, with no validation or logic, gives away almost as much as a public field — it is the control the setter exercises, not the mere existence of the function, that is the benefit.

Q2. Why does printBalance() above require getBalance() to be const?

Ans: Because printBalance() takes its argument as const Wallet& w. Through a reference to const, the compiler permits only operations that are guaranteed not to modify the object — and the only member functions that carry that guarantee are those marked const. A non-const member function is free to change the object's state, so calling it on a const object would break the promise the const reference makes; the compiler therefore rejects it outright. Marking getBalance() as const puts const on the hidden this pointer (it becomes const Wallet*), which is exactly the qualification needed for the call to bind.

class Wallet {
    double balance;
public:
    explicit Wallet(double b) : balance(b) {}
    double getBalance() const { return balance; }   // const: callable on const objects
    void deposit(double a) { balance += a; }        // non-const: NOT callable on them
};

void printBalance(const Wallet& w) {
    cout << w.getBalance();   // OK
    // w.deposit(10);         // ERROR: passing 'const Wallet' as 'this' argument
}                             //        discards qualifiers

This is the practical heart of const correctness. It matters because passing large objects by const reference is the standard way to avoid a copy while promising not to alter the caller's object; if the class's read-only functions are not marked const, that idiom becomes unusable and callers are forced either to copy the object or to drop const everywhere — "const poisoning" spreading through the codebase. Rule of thumb: mark every member function that does not modify the object as const, and do it when you first write the class. Related trap: const is part of the signature, so getBalance() and getBalance() const can coexist as overloads, and the compiler picks by whether the object is const.

Q3. What does "implicitly inline" mean for a function defined inside a class body?

Ans: A member function whose body is written inside the class definition is automatically treated as if it had been declared with the inline keyword — you do not have to write inline yourself. This has two consequences. First, a linkage one, which is the real reason the rule exists: class definitions live in headers included by many .cpp files, so without implicit inline every translation unit would emit its own definition of the function and the linker would reject it as a multiple definition. inline permits identical definitions in several translation units and tells the linker to keep just one. Second, an optimisation hint: the compiler may replace the call with the function body, removing call overhead — but this is only a suggestion, and modern compilers decide for themselves based on size and complexity.

class Student {
    string name; float marks;
public:
    // Defined INSIDE the class body -> implicitly inline
    float getMarks() const { return marks; }

    // Only DECLARED here -> NOT inline; defined elsewhere
    void setMarks(float m);
};

// Defined outside the class, in a .cpp file -> ordinary external linkage
void Student::setMarks(float m) { if (m >= 0 && m <= 100) marks = m; }

// If you must define it outside but keep it in a HEADER, say so explicitly:
// inline void Student::setMarks(float m) { ... }

Points examiners look for:

  • Implicit inline applies to member functions defined inside the class, and also to friend functions defined inside the class body.
  • inline never changes the function's meaning or its access — only its linkage and an optimisation hint.
  • Keep inline bodies short (getters, setters, one-line helpers); a large inline function bloats every caller and slows compilation.
  • MCQ trap: inline is a request, not a command — the compiler is free to ignore it, and it may equally inline functions you never marked.

📌 Sample & Repeated FPSC Questions (2016–2026)

2018, Paper I, Q4(b) (5 marks) — Which class(es) can access private members of class C directly?

Only class C itself — via its own member functions — can directly access C's private members. Even classes D and E that derive from C cannot access C's private members directly; they can only reach them indirectly through C's own public/protected member functions (accessors/mutators), unless C explicitly declares D, E, or some function as a friend (not stated here). So the answer is: C's own member functions only (plus any explicitly declared friends).

Key reasoning points:

  • This is exactly why accessor ("getter") and mutator ("setter") member functions exist — as covered above, they're the only sanctioned route for outside code to reach private data.
  • A derived class inherits access to C's protected members, but not its private ones — a very commonly tested distinction, and precisely what this question checks.

2025, Paper I, Q7 (1 mark, MCQ) — Which programming language uses the concept of friend function?

  • A) Python
  • B) Java
  • C) C#
  • D) None of these ✓

Explanation: The friend function/class mechanism — letting a non-member function access a class's private/protected members — is a C++-specific feature; Python, Java, and C# have no equivalent.

MCQ Trap: a friend function is deliberately not a member function of the class it's declared inside (it's defined and called like an ordinary function) — don't confuse "granted private access" with "is a member function."

📜 Past-Paper Questions & MCQs (CSS 2016–2026)

2020 · Paper-I · Q4(a) · 8 marks

Q. Write a program to add, subtract and multiply complex numbers. Each operation should be a separate method.

Ans.

#include <iostream>
using namespace std;
class Complex {
    float re, im;
public:
    Complex(float r = 0, float i = 0) : re(r), im(i) {}
    Complex add(Complex o)      { return Complex(re + o.re, im + o.im); }
    Complex subtract(Complex o) { return Complex(re - o.re, im - o.im); }
    Complex multiply(Complex o) { return Complex(re * o.re - im * o.im, re * o.im + im * o.re); }
    void show() { cout << re << (im < 0 ? " - " : " + ") << (im < 0 ? -im : im) << "i\n"; }
};
int main() {
    Complex a(3, 2), b(1, 4);
    a.add(b).show();        // 4 + 6i
    a.subtract(b).show();   // 2 - 2i
    a.multiply(b).show();   // -5 + 14i
}
  • Formula: (a + bi)(c + di) = (ac − bd) + (ad + bc)i.