Effective C++ : Chapter 4 Note

All notes are my personal thoughts on the Book Effective C++ Third Edition by Scott Meyers. They do not represent the opinions of any affiliations.

Function Design

The first thing the book talks about function design is to make it hard to use incorrectly. Code consistency is always needed to write good code, so it will not be discussed here. In addition, making the behavior of code compatible with built-in types is also important. For example,

if(a * b = c){
    // Do something
}

There is really no reason this should compile. Hence, making the operator return an object with a const qualifier would be appropriate. Besides, managing potential memory leak would also be important. When returning a pointer just created, the code should consider potential memory leak or error caused by the deletion of the same pointer twice. This problem could be solved by using a smart pointer, which would automatically release the memory.

Moreover, people should prefer pass-by-reference-to-const when writing a function instead of pass-by-value. This is generally true for large objects since it prevents objects from copying which would require extra time and memory. However, for some objects, such as int or char, there is really no reason to use it since the address of a data would also be 64 bits (on a relatively modern machine).

void display_info(Info info){  // Constructor is called
    // Print info code
}

void display_info_cref(const Info& info){  // No constructor called
    // Print info code
}

The reference-rule does not always apply. For example, when returning an object, don’t always try to return a reference to an object. It may cause memory error when trying to return a local object or a memory leak if the user forgets to delete the object.

int& multiply1(int x, int y){
    int z = x * y;
    return z;              // Trying to return a reference to local object - ERROR!
}

int& multiply2(int x, int y){
    int *z = x * y;
    return *z;            // Potential Memory Leak!
}

Class Design

Some of the rules in the function design section above also applied to class design, and there are more to it included in this chapter. The first general rule is to declare data members private. This prevents users from getting confused with function names if there are overlapping names. Moreover, it makes the encapsulation of code more robust, thereby improving security and flexibility.

The book also gave two general design advice for classes:

- Prefer non-member non-friend functions to member functions
- Use non-member function type when type conversions should apply to all parameters

The reason for the first advice is really about encapsulation and flexibility. People could simply put various utility functions in the same namespace. The second example could be best explained using the following example.

class Complex{
public:
    // Some code
    Complex(int a = 0, int b = 0); // Initialize a complex number with two int
    const Complex operator+(const Complex& b){
        return Complex(x + b.x, y + b.y);
    }
private:
    // Private data member ...
};

int main(){
    Complex x;
    auto y = x + 3; // OK, 3 became Complex(3,0)
    auto z = 3 + x // Error! 3 is not a Complex and failed to convert
}

When the code proceeds to $3 + x$, it failed since x cannot be converted to an int, and 3 cannot be converted to a Complex. Adding an implicit conversion-to-int function in Complex really does not solve the problem since it would cause the compiler to fail to execute x + 3 due to type ambiguity. The correct way to approach this problem is to use a non-member function.

const Complex operator+(const Complex& a, const Complex& b){
    return a + b;
}

Through writing in this way, an int object would first be converted to a Complex object. Then, the member function of operator + could be called.

Finally, the last section talks about how to provide a useful swap. A swap function should not throw exceptions. The steps to achieve is to first provide a public swap member function, then a non-member swap function calling the member-swap, and then write a specialized std::swap (not for class template).

In this chapter, the author also, in fact, talks about various things to think about when writing a class. This part will be discussed in other posts.

Leave a Reply

Your email address will not be published. Required fields are marked *