What I Learned from Scott Meyers' Effective C++, Part 4

Chapter 4: Designs and Declarations

In my previous chapter, we discussed Resource Management. In this chapter, we'll talk about what I learned in chapter 4 of the book, items 18 to 25.

Item 18: Make interfaces easy to use correctly and hard to use incorrectly

This item title does not leave much for an explanation. You should always strive to develop interfaces that are easy to use correctly and difficult to use incorrectly. Taking a look at the built-in types and STL implementations and following similar approaches is a good way to ensure this. Creating new types to define behaviour, restricting certain operations, constraining possible values and eliminating resource management responsibility from the client side are some examples of achieving this.

Item 19: Treat class design as type design

Creating a class in C++ should start with a multitude of questions answered about your design almost as if you were defining a type in the core of the language. There are many pitfalls in class design in C++ and starting with a comprehensive list of questions and answers is crucial in creating robust classes.

Item 20: Prefer pass-by-reference-to-const to pass-by-value

Copying is often an expensive operation in C++ and passing by value will recursively call copy constructors within the passed item. Instead of this possibly expensive approach, passing items by reference to const allows you to avoid this and ensure that the passed object is not modified by the user. Also, a derived class passed by value as a base class type will be sliced, meaning it will lose its specialized data causing a big mess for you to clean up. Pass by reference to const helps you avoid this potentially catastrophic issue as well. However, there are some types such as built-in types and STL iterators that are okay to pass by value and actually should be passed by value.

Item 21: Don't try to return a reference when you must return an object

When programmers learn about the previous item they tend to hold on to it religiously, incorrectly. You should not return a pointer or a reference to a local stack object, a reference to a heap-allocated object or a pointer/reference to a local static object. It is highly likely that those will be invalidated before you are done using them.

Item 22: Declare data members private

Implementing your data members private is like building a dam. Once the gates are open and clients start using your code, it becomes exponentially expensive to change your implementation. Providing fine-grained control and uniform access to your data lets you build a dam early and keep the gates closed and gives you implementation flexibility. Also, protected does not provide more encapsulation than public. Use cautiously.

Item 23: Prefer non-member non-friend functions to member functions

Preferring non-member non-friend functions over member functions provides you with increased encapsulation and packaging flexibility.

Item 24: Declare non-member functions when type conversions should apply to all parameters

If you need ype conversion on all parameters of a function, it requires that you declare a non-member function.

Item 25: Consider support for a non-throwing swap

It is okay to specialize std templates like swap for your own types, but never add something completely new to std as it is dictated by the C++ committee. It's often a good idea to provide a member swap function for types that std::swap is inefficient or not applicable. std::swap is likely to call many copy constructors. If you implement your own swap implementation make sure that it does not throw exceptions.

That's it for this chapter, next article will be about Chapter 5, Implementations