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
- What's the difference between declaration and definition? What do these look like for objects, functions, classes, and templates?
- What are the signatures for the copy constructor and the copy assignment operator? When is each called?
- What does the
explicit
qualifier do for constructors? Why might we use it?
Item 1: View C++ as a federation of languages.
- What are the four sub-languages of C++, and why the distinction?
Item 2: Prefer consts, enums, and inlines to #defines.
- What are two potential problems with using macro constants, as in
#define ASPECT_RATIO 1.653
? - What's the difference between the following (are they all allowed?),
const char * const greeting = "Hello";
,char * const greeting = "Hello";
,char const * const greeting = "Hello";
char const * greeting = "Hello";
- Give an example of providing an initial value for a static class member at its points of declaration.
- Can a static class member be a double? If so, are there any caveats?
- Can a static class member be both const and non-const? Are there any differences between their declaration?
- Is this allowed in a class declaration,
static int count = 2;
? - Say you declare
static const int count = 2;
in a class namedWidget
. Can you later take the address of count, as inconst int* pa = &w.count;
? Are there any caveats? - What are some problems with this
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
? - What is undefined behaviour? Provide an example.
Item 3: Use const whenever possible.
- What is the syntax for an iterator for a vector where the iterator and the data cannot be changed?
- Give an example where a
const
return type is preferable. - 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) {...}
- 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
- What does
const
at the end of a function signature indicate? - What is the difference between bitwise constness and logical constness?
- Why is the assignment to
easy_text
an error, but forhard_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'; } };
- How can you qualify a member variable to allows its modification by
const
functions? Why would you want to do this? - 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.
- Is
int x;
guaranteed to be initialized? - Is it okay to read an uninitialized variables? If not, what can happen?
- How can we remedy the fact that initialization doesn't always occur by default?
- Does initialization occur before or after the constructor body? Is it the same for built-in types?
- If a member object of a user-defined type is not in the initialization list, what initializer is called?
- What determines the order of initialization of member variables?
- What's the rational behind matching declaration order to constructor member initialization order?
- What is a static object?
- What determines if a static object is local or not?
- 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.
- Which class functions will the compiler provide for you?
- In what case will the compiler define a default constructor for you?
- Write the equivalent definition (after compilation) for the following class.
struct Info { std::string name; int number; };
- Which functions will the compiler generate for the following class? Does anything demand user definition?
struct Info { std::string name; const int number; ... };
- 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.
- Write a
NotCopyable
base class the can be derived from to prevent copying.
Item 7: Declare destructors virtual in polymorphic base classes.
What is the purpose of the
virtual
member function qualifier?Is it okay to call a non-virtual destructor for a derived-class using a pointer to its base-class?
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; }
What if we make
Base::makeNoise
virtual?Since the STL container types have non-virtual destructors, why is it a bad idea to derive from them?
What makes a given base class a polymorphic base class?
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.
- 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
- What are two ways to "swallow" exceptions in a destructor?
Item 9: Never call virtual functions during construction or destruction.
- 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; }
- How might we rewrite the example above to work as one might intend?
Item 10: Have assignment operators return a reference to *this.
- 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=.
- The
Bitmap
copy constructor may throw. Write a copy assignment operator forWidget
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.
- 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.
- What does RAII stand for?
- What are the two main types of smart pointers?
- Will this compile? If not, why?
std::unique_ptr<int> p1(new int); std::unique_ptr<int> p2 = p1;
- Write a resource-managing object for a single integer.