Effective C++:Chapter 1 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.

Introduction to This Note

​ Writing efficient and correct C++ code is a difficult thing that can even challenge many experienced coders. As a student deeply interested in C++, I felt the need to start advancing my skills (people are just so damn good). So, I decided to open up this series. But, I have to say that (to the few audiences if I do have any) I am NEITHER a good coder NOR a proficient writer. I am just treating this as a studying project and reflect some of my thoughts, and I really welcome comments. Since each chapter contains several items, I will try to make connections between them and put together essential points, though the order might be different. Let’s begin~

Overview of C++ As A Programming Language

​ Since Item 1 is really a sort of overview of C++ as a programming language, I will just skim through this part. The author’s idea was really intriguing. The author pointed out that, instead of viewing C++ as a single programming language, we should view it as a “federation of related languages“.

Four primary related languages according to the author:

  • C
    • C++ includes basic C mechanism
  • Object-Oriented C++
    • C++ was originally called *C with Classes*
  • Template C++
    • Offered Programmer more flexibility and functionality. Enabled *template metaprogramming*, or TMP.
  • The STL
    • The standard template library is a special set of libraries that provided containers, algorithms, etc.

A good program would use different and appropriate sets of skills depend on the context.

Use of const

​ There are different types of const for pointers. In the C++ Primer Book, it was called the low-level const and top-level const.

  • Top-level const indicates that the pointer itself is a const. The const syntax appears on the right of the asterisk.
  • Low-level const indicates that the object that the pointer points to is const. The const syntax appears on the left of the asterisk.

Many old programs tend to have code like the following:

#define PI 3.14
#define PI2 6.28

The book suggests using const values to replace the #define.

const double PI = 3.14;
const double PI2 = 6.28;

As C++ 11 came out, a new mechanism was introduced: constexpr. It can be used to replace const when used as a constness indicator on a variable under the premise that the initializer expression to be computable at compile time. Pointers with constexpr are applied with a top-level const. When using constexpr on a function, it could be used to execuate consuming code at compile time (There are many limitations, we will discuss this specifically later). In C++17, constexpr could also be used to evaluate a condition. The following is an example from cppreference.

template <typename T>
auto get_value(T t) {
    if constexpr(std::is_pointer_v<T>) return *t; // deduces return type to int for T = int*
    else return t; // deduces return type to int for T = int
}

​ The value of the condition must be “a contextually converted constant expression of type bool”. One of the statements would be discarded depending on the value of the expression. There are also many exceptions to this, and we will discuss it thoroughly later.

​ Back to the discussion of const, const could be applied to functions. There are two ways to add const to functions. By adding before a function’s declaration and after the declaration.

const int foo(int x,int y){
    return x + y;
}

class Bar{
    // Some Functions....
    int bar(int x, int y) const{
        return x + y;
    }
}

​ Adding before the function merely alter the return type of the function. In the book, author gives the following example to illustrate some application of the const modifier on a function.

class Rational{...};
const Rational operator*(const Rational& lhs, const Rational& rhs);
int main(){
    Rational x,y;
    // Initialize x, y
    (a * b) = c; // This will result in a compile error
                 // If there is not const, it would be allowed
}

​ The const modifier efficiently reduced potential grammatical errors typed by the programmers.

​ However, adding after the declaration was to restrict the function from changing any non-mutable objects of a class (Note that this type of const could only be used when declaring a member function). Note that non-const function could only be called by non-const object.

Enum Hack and Its Replacement

​ Often, in a class, there are needs to define certain const values that holds useful informations. For example, the following code.

class MyId{
    /* .... */
    private:
        const int id_len = 15;
        int id[15];
}

​ However, this would result a copy of id_len in all of the MyId objects. When the number of such const values increases, the extra memory cost was quiet quite considerable. Hence, programmers used the enum hack to help resolve the problem.

class MyId{
/* .... */
private:
    enum { id_len = 15 };
    int id[id_len];
}

 

​ Since modern compilers now support in-place initialization of the value, there is an alternative choice by using static const.

class MyId{
    /* .... */
private:
    static const int id_len = 15;
    int id[id_len];
}

 

​ Modern compilers would also optimize the constant so that no extra memory is taken.

Inline Functions vs. Macro Functions

​ Programmers use macros to define functions, but it sometimes causes lots of troubles. The following code illustrates an example.

#define MAX(a, b) (a > b ? a : b);

​ The macro defines a function that was intended to return the maximum value between two numbers. However, when used with following conditions, it results in an unexpected return values.

int a = 1;
int b = 2;
int c = MAX(++a, b)
std::cout<<c<<std::endl; // This will return 4

​ The reason that our variable c is 4 is that the expression ++a was copied to the macro defined function. To resolve the problem, we have to put parenthesis around the expression. An alternative to the macro defined function was inline function. Inline function will be expanded in line when it is called. It is similar to the macro but can eliminate errors as demonstrated above. People should use inline to replace macro functions as many as possible.

template<typename T>
inline T MAX(const T& a, const T& b){
    return a > b ? a : b;
}

Object Initialization

​ Objects in C++ need to be initialized. When we write a constructor, we sometimes have code like the following:

class Person{
    Person(const string& _name, const int _age){
        Name = _name; // These are assignments
        Age = _age;
    };
    /* .... */
}

However, this is not the best approach. The two variable: Name and Age are being assigned. The process of initialization, which would use default constructors, takes place before the assignment. However, this sometimes causes troubles especially dealing with pointers. The book suggests to use member initialization list to initialize objects members.

class Person{
    Person(const string& _name, const int _age):Name(_name),Age(_age){

    };
    /* .... */
}

PS

​ It has been a long time since my previous post. Lots of things happened. High school year is ending now. Hopefully, I can finish this series before I graduate. See you next time~ 🙂

Leave a Reply

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