jtag

Effective C++ – Hour 10

This post marks 10 hours of working through Effective C++ by Scott Meyers.

My motivation for choosing this book comes from the fact that I'm currently in need of a job. My hope is that furthering my C++ skills, which I used in most of my internships, is one of the most efficacious things I can do for my job search. Also, in the past, I have quite liked reading through the annals of cppreference.com, so I at least do enjoy learning ephemeral intricacies.

Throughout my 10 hours, I read up to Chapter 3, Item 15. I learned about a number of technical details relating to object construction/destruction, as well as a bit on resource management. There was a lot of stuff I would like to say I already knew. In actuality, had I not read this book, I would be likely to make many of the mistakes it warns of. For example, Item 7 asks us to "declare destructors virtual in polymorphic base classes", for the reason that only member functions declared virtual will respect polymorphic overloading when called through a base class pointer. This is something I really ought to have known.

To test my future knowledge, I prepared test questions corresponding to each Item that I read. I only answered a few of them myself, but I plan to go through them if/when I return to this book. My questions will be attached below.

All in all, I found the experience to be fair. There is nothing overly exciting about technicalities, but I think the content's mundanity is outweighed by its practicality, and I do enjoy knowing useful things. Right now, I see C++ as my primary avenue for some financial success, and so I feel that this book has increased my value therein, which is a big positive for me.


Self-test Questions

Introduction

  1. What's the difference between declaration and definition? What do these look like for objects, functions, classes, and templates?
  2. What are the signatures for the copy constructor and the copy assignment operator? When is each called?
  3. What does the explicit qualifier do for constructors? Why might we use it?

Item 1: View C++ as a federation of languages.

  1. What are the four sub-languages of C++, and why the distinction?

Item 2: Prefer consts, enums, and inlines to #defines.

  1. What are two potential problems with using macro constants, as in #define ASPECT_RATIO 1.653?
  2. What's the difference between the following (are they all allowed?),
    1. const char * const greeting = "Hello";,
    2. char * const greeting = "Hello";,
    3. char const * const greeting = "Hello";
    4. char const * greeting = "Hello";
  3. Give an example of providing an initial value for a static class member at its points of declaration.
  4. Can a static class member be a double? If so, are there any caveats?
  5. Can a static class member be both const and non-const? Are there any differences between their declaration?
  6. Is this allowed in a class declaration, static int count = 2;?
  7. Say you declare static const int count = 2; in a class named Widget. Can you later take the address of count, as in const int* pa = &w.count;? Are there any caveats?
  8. What are some problems with this #define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))?
  9. What is undefined behaviour? Provide an example.

Item 3: Use const whenever possible.

  1. What is the syntax for an iterator for a vector where the iterator and the data cannot be changed?
  2. Give an example where a const return type is preferable.
  3. Can these two functions exist in the same namespace? Why or why not?
    • const char& operator[](std::size_t position) {...}
    • char& operator[](std::size_t position) {...}
  4. Can these two functions exist in the same namespace? Why or why not?
    • char& operator[](std::size_t position) {...}
    • const char& operator[](std::size_t position) {...} const
  5. What does const at the end of a function signature indicate?
  6. What is the difference between bitwise constness and logical constness?
  7. Why is the assignment to easy_text an error, but for hard_text it's not?
    struct TextBlock {
        char easy_text[5];
        char* hard_text = new char[5];
    
        TextBlock() {
            easy_text[0] = 'A';
            hard_text[0] = 'A';
        }
    
        void replace() const {
            easy_text[0] = 'B';
            hard_text[0] = 'B';
        }
    }; 
    
  8. How can you qualify a member variable to allows its modification by const functions? Why would you want to do this?
  9. Explain what's happening in this code block.
    const char& operator[](int pos) const {
        return data[pos];
    }
    
    char& operator[](int pos) {
        return const_cast<char&>(
            static_cast<const Text&>(*this)[pos]
        );
    }
    

Item 4: Make sure that objects are initialized before they’re used.

  1. Is int x; guaranteed to be initialized?
  2. Is it okay to read an uninitialized variables? If not, what can happen?
  3. How can we remedy the fact that initialization doesn't always occur by default?
  4. Does initialization occur before or after the constructor body? Is it the same for built-in types?
  5. If a member object of a user-defined type is not in the initialization list, what initializer is called?
  6. What determines the order of initialization of member variables?
  7. What's the rational behind matching declaration order to constructor member initialization order?
  8. What is a static object?
  9. What determines if a static object is local or not?
  10. Is this defined or undefined: the relative order of initialization of non-local static objects in different translation units. If not, what's a solution?

Item 5: Know what functions C++ silently writes and calls.

  1. Which class functions will the compiler provide for you?
  2. In what case will the compiler define a default constructor for you?
  3. Write the equivalent definition (after compilation) for the following class.
    struct Info {
        std::string name;
        int number;
    };
    
  4. Which functions will the compiler generate for the following class? Does anything demand user definition?
    struct Info {
        std::string name;
        const int number;
        ...
    };
    
  5. Give two cases where a compiler won't default generate a copy assignment operator.

Item 6: Explicitly disallow the use of compiler-generated functions you do not want.

  1. Write a NotCopyable base class the can be derived from to prevent copying.

Item 7: Declare destructors virtual in polymorphic base classes.

  1. What is the purpose of the virtual member function qualifier?

  2. Is it okay to call a non-virtual destructor for a derived-class using a pointer to its base-class?

  3. What is the output of the following.

    class Base {
        public:
        void makeNoise() {
            std::cout << "Base!" << std::endl;
        }
    };
    
    class Derived: public Base {
        public:
        void makeNoise() {
            std::cout << "Derived!" << std::endl;
        }
    };
    
    int main() {
    
        Derived a;
        Base& b = a;
        Base* pa = &a;
    
        a.makeNoise();
        b.makeNoise();
        pa->makeNoise();
    
        return 0;
    }
    
  4. What if we make Base::makeNoise virtual?

  5. Since the STL container types have non-virtual destructors, why is it a bad idea to derive from them?

  6. What makes a given base class a polymorphic base class?

  7. What's the point of this class, and why is the destructor definition necessary?

    class AbstractBase {
        public:
        virtual ~AbstractBase() = 0;
    };
    
    AbstractBase::~AbstractBase() {}
    

Item 8: Prevent exceptions from leaving destructors.

  1. Is there anything wrong here?
    class Widget {
        public:
        ...
        ~Widget() { ... } // assume this might emit an exception
    };
    void doSomething()  {
        std::vector<Widget> v;
        ...
    } // v is automatically destroyed here
    
  2. What are two ways to "swallow" exceptions in a destructor?

Item 9: Never call virtual functions during construction or destruction.

  1. What will this output?
    struct Base {
        virtual void saySomething() { std::cout << "Base!" << std::endl; }
        Base() { saySomething(); }
    };
    
    struct Derived : public Base {
        virtual void saySomething() { std::cout << "Derived!" << std::endl; }
    };
    
    int main() {
        Derived d;
        return 0;
    }
    
  2. How might we rewrite the example above to work as one might intend?

Item 10: Have assignment operators return a reference to *this.

  1. Implement copy-assignment for the Number class so that this works is expected.
    Number x,y;
    x = y = 10;
    

Item 11: Handle assignment to self in operator=.

  1. The Bitmap copy constructor may throw. Write a copy assignment operator for Widget that safely handles self-assignment and exceptions.
    class Bitmap {...}; // Copy constructor may throw.
    class Widget { Bitmap* pb; };
    

Item 12: Copy all parts of an object.

  1. What's missing from this custom copy constructor?
    Derived& operator=(const Derived& rhs) { 
        //
        derivedVal = rhs.derivedVal; 
        return *this; 
    }
    

Item 13: Use objects to manage resources.

  1. What does RAII stand for?
  2. What are the two main types of smart pointers?
  3. Will this compile? If not, why?
    std::unique_ptr<int> p1(new int);
    std::unique_ptr<int> p2 = p1;
    
  4. Write a resource-managing object for a single integer.

Item 14: Think carefully about copying behaviour in resource-managing classes.

Item 15: Provide access to raw resources in resource-managing classes.