Concepts / Inline Functions and Performance in Class Design

Inline Functions and Performance in Class Design

A user-defined arithmetic type such as complex packages a mathematical value together with the operations that act on it inside one class.

  • Programming

Starting From the Class Listing

A user-defined arithmetic type such as complex packages a mathematical value together with the operations that act on it, all inside one class. Rather than starting from a definition, this article starts from the code itself. Read the class below once without worrying about every detail. Then, as you go through the sections, you will learn to locate each part by its job: storage, access, construction, arithmetic, and performance.

python
Notes on the code
  • re and im are the two double members that hold the state
  • the constructors set those members; they are not shown being called in this snippet
  • real() and imag() are accessors that read the members
  • operator+= modifies the object in place and returns a reference to it

This listing is an illustrative reconstruction written to match the structure the source describes: two double members, a constructor that takes a single scalar, accessors for the real and imaginary parts, and a compound assignment operator. The source's own class is the reference point; the snippet is a teaching aid that follows the same pattern. Use it to practice finding each part of the class by its role.

Where the Data Lives

The complex class stores its state in two double members, named re and im. These sit before the public label, which means other code cannot reach them directly. Everything after the public label is the interface, the set of operations that outside code is allowed to use. This split between hidden data and public operations is the core of the design. The data is the representation: the internal structure that holds one complex value. The public functions are the only doorway to that data.

The entire stored state of one complex object is therefore two double fields and nothing else. Every constructor, getter, setter, and operator in the class works by reading or writing these two fields. A complex value is just a pair of doubles that the class agrees to interpret as a real part and an imaginary part. When you are unsure what a complex value is, return to that picture.

complexone classre, imtwo double members, hiddenreal(), imag()accessors that read thedatapublic interfacewhat other code may use+=, -=arithmetic defined in theclass*=, /=declared here, definedelsewhereholdsexposesincludesincludesincludes
Which members hold the data, which expose it to callers, and which perform the arithmetic? All of them belong to the one complex class.

The source explains that the class definition contains only the operations that need direct access to the representation. Operations that can be written using the public interface, or that are less central, are placed elsewhere. This is why multiply-assign and divide-assign appear only as declarations: their bodies live outside the class. The accessors real and imag are members too, so they share the same access to the representation.

Tracing a Constructor and a Compound Assignment

Work through a small trace with the class above. Suppose the first line creates a complex object from one scalar, and the second line applies += with a second complex value. The first line's result is decided entirely by the one-scalar constructor. The second line's result is decided entirely by the body of += in the class. The body of += does not mention the constructor at all. It only reads the two members of its argument and adds them to the members of the object it was called on. That is why the operator can be understood without knowing how the temporary was built.

python
Notes on the code
  • the one-scalar constructor sets the starting state
  • complex(1, 2) builds a temporary whose re is 1 and im is 2
  • += adds 1 to re and 2 to im, changing a in place

What do you think happens?

After a is built from the scalar 3 and then a += complex(1, 2) runs, what are the stored re and im of a?

Reveal answer
Answer:

re is 4 and im is 2

The constructor sets re to 3 and im to 0. The += body adds the argument's re (1) to re, giving 4, and adds the argument's im (2) to im, giving 2. Only the two members of a change.

after constructorre = 3, im = 0after +=re = 4, im = 2a += complex(1, 2)
After the constructor sets re and im, the compound assignment changes both members of the same object. Which member changed, and what are the new values?

Both members changed because the operator body touches both re and im. The object itself was modified; no new object was created to hold the sum. That in-place behavior is what the compound operator is for.

Why += Returns a Reference

Inside the body of operator+=, the names re and im refer to the members of the object on the left of the += sign. The parameter z is the value on the right. Because the function is a member, it has direct access to the representation, which is exactly what it needs to add two complex values part by part.

The return type of += and -= is complex&, a reference to the object itself. The body ends by returning the dereferenced this pointer, so the function hands back the very object it modified. Returning a reference rather than a copy is what allows the result of one compound assignment to be used directly in another expression, while the original object is still changed in place.

python
Notes on the code
  • a += complex(1, 2) returns a reference to a, not a new object
  • the reference lets .imag() be called on the updated a directly
  • a is changed; no copy of the result was made
callercode that writes a += baobject on the left of +=bvalue on the right of +=1. call operator+=(b)2. read b.re and b.im3. return complex& to a4. next operation uses the returned reference
In what order do the steps happen, and how does the returned reference let the result feed directly into the next operation on the same object?

What Inlining Changes and What It Does Not

The source explains that a complex type has to be efficient, or programmers will not use it. Arithmetic on a number type is performed constantly, so the overhead of each operation matters. For this reason, simple operations such as constructors, +=, and imag() are expected to be implemented without function calls in the generated machine code. This is what inlining means: the body of the function is placed where it is used instead of being reached through a call.

Two facts about inlining apply to this class. First, functions defined inside a class are inlined by default, so the short bodies in the class listing benefit without extra work. Second, the keyword inline can be written before a function declaration to require inlining explicitly. Either way, inlining is a performance decision. The class returns the same values whether or not a particular function is inlined.

The source also notes that a full industrial-strength complex, such as the one in the standard library, is carefully implemented to do appropriate inlining. The class you have been reading is a small teaching version, and its performance properties follow from the same idea on a smaller scale.

Mistakes When Reading Arithmetic Classes

  • Common mistake

    Assuming += creates a new complex object for the sum

    What it looks like
    complex a(3); a += complex(1, 2);
    Why it fails

    The body of operator+= modifies the members of the object it was called on and returns a reference to it.

    Do this instead

    Read a += b as changing a in place, so a itself holds the new re and im afterward.

  • Common mistake

    Looking for the += logic inside the constructor

    What it looks like

    Tracing how complex(1, 2) was built to explain the sum

    Why it fails

    The += body only reads the two members of its argument, so it does not depend on how the temporary was constructed.

    Do this instead

    Trace the constructor for the starting state and the operator body for the change, separately.

  • Common mistake

    Treating the data members as public because they are declared in the class

    What it looks like

    Writing a.re directly from code outside the class

    Why it fails

    The members re and im sit before the public label, so outside code reaches them only through the public interface.

    Do this instead

    Use real(), imag(), or the declared operators to read or change the value.

  • Common mistake

    Believing an inlined function can return a different value

    What it looks like

    Expecting imag() to give a different answer once it is inlined

    Why it fails

    Inlining changes where the function body is placed in the generated code, not what the body computes.

    Do this instead

    Compare results by inputs and the function's definition; inlining is a performance property.

  • Common mistake

    If an arithmetic expression fails to compile, the operator used may not be declared in the class; check the public interface first

Practice Tracing a Complex Object

MEDIUM

Using the class above, trace this sequence: complex a(5); then a += complex(2, -1); then a += complex(0, 4). What are the final re and im of a, and which members changed on each += call?

Hints
  • The constructor complex(5) sets re to 5 and im to 0.
  • Each += adds the argument's re to re and the argument's im to im.
  • Track re and im separately across both += calls.
MEDIUM

Explain, in two or three sentences, why a += b returning complex& lets the code write (a += b).imag() without making a copy, and say which object imag() reads.

Hints
  • Think about what the function returns: a reference to the object it modified.
  • Ask which object is updated when the sum is computed.

Key Takeaways

  • A user-defined arithmetic class such as complex keeps its state in hidden double members, re and im, and exposes them only through its public interface.
  • Compound operators such as += modify the object in place, and they return a reference to that object so that the result can be used directly in another expression.
  • Each part of the class has a job: data members store the value, accessors and operators form the interface, and the operator bodies perform the arithmetic.
  • Simple operations such as constructors, +=, and imag() are expected to be inlined for performance; functions defined inside a class are inlined by default, and the inline keyword can require it explicitly.
  • Inlining changes how the generated code reaches a function body, not the value the class returns to the caller.

Related concepts

More concepts starting with I