# Chapter 15. Object-Oriented Programming
## 15.1 OOP: An Overview
The key ideas in object-oriented programming are data abstraction, inheritance, and dynamic binding.
Classes related by inheritance form a hierarchy. Typically there is a base class at the root of the hierarchy, from which the other classes inherit, directly or indirectly. These inheriting classes are known as derived classes.
The base class defines as virtual those functions it expects its derived classes to define for themselves.
A derived class must specify the class(es) from which it intends to inherit. It does so in a class derivation list, which is a colon followed by a comma-separated list of base classes each of which may have an optional access specifier.
In C++, dynamic binding happens when a virtual function is called through a reference (or a pointer) to a base class.
## 15.2 Defining Base and Derived Classes
### 15.2.1 Defining a Base Class
Base classes ordinarily should define a virtual destructor. Virtual destructors are needed even if they do no work.
A function that is declared as virtual in the base class is implicitly virtual in the derived classes as well.
A derived class inherits the members defined in its base class. However, the member functions in a derived class may not necessarily access the members that are inherited from the base class. Like any other code that uses the base class, a derived class may access the public members of its base class but may not access the private members. However, sometimes a base class has members that it wants to let its derived classes use while still prohibiting access to those same members by other users. We specify such members after a protected access specifier.
### 15.2.2 Defining a Derived Class
A derived class must specify from which class(es) it inherits. It does so in its class derivation list, which is a colon followed by a comma-separated list of names of previously defined classes.
If a derived class does not override a virtual from its base, then, like any other member, the derived class inherits the version defined in its base class.
Because a derived object contains subparts corresponding to its base class(es), we can use an object of a derived type as if it were an object of its base type(s). In particular, we can bind a base-class reference or pointer to the base-class part of a derived object.
This conversion is often referred to as the derived-to-base conversion.
The fact that a derived object contains subobjects for its base classes is key to how inheritance works.
A derived class must use a base-class constructor to initialize its base-class part. Each class controls how its members are initialized.
The base class is initialized first, and then the members of the derived class are initialized in the order in which they are declared in the class.
If a base class defines a static member, there is only one such member defined for the entire hierarchy.
```cpp
class Base {
public:
static void statmem();
};
class Derived : public Base {
void f(const Derived&);
};
```
A derived class is declared like any other class. The declaration contains the class name but does not include its derivation list:
```cpp
class Bulk_quote : public Quote; // error: derivation list can't appear here.
class Bulk_quote;
```
A class must be defined, not just declared before we can use it as a base class.
Sometimes we define a class that we don't want others to inherit from. Or we might define a class for which we don't want to think about whether it is appropriate as a base class. Under the new standard, we can prevent a class from being used as a base by following the class name with `final`:
```cpp
class NoDerived final;
```
### 15.2.3 Conversion and Inheritance
Like built-in pointers, the smart pointer classes support the derived-to-base conversion - we can store a pointer to a derived object in a smart pointer to the base type.
The static type of an expression is always known at compile-time, it is the type with which a variable is declared or that an expression yields. The dynamic type is the type of the object in memory that the variable or expression represents. The dynamic type may not be known until run time.
It is crucial to understand that the static type of a pointer or reference to a base class may differ from its dynamic type.
The is no automatic conversion from the base class to its derived class(s):
```cpp
Quote base;
Quote *itemP = &bulk; // ok: dynamic type is Bulk_quote
Bulk_quote *bulkP = itemP; // error: can't convert base to derived
```
It is often possible to convert an object of a derived class to its base-class type. However, such conversions may not behave as we might want.
```cpp
Bulk_quote bulk;
Quote item(bulk); // uses the Quote::Quote(const Quote&) constructor
item = bulk; // calls Quote::operator=(const Quote&)
```
Because the `Bulk_quote` part is ignored, we say that the `Bulk_quote` portion of `bulk` is sliced down.
When we initialize or assign an object of a base type from an object of a derived type, only the base-class part of the derived object is copied, moved, or assigned. The derived part of the object is ignored.
## 15.3 Virtual Functions
There are three things that are important to understand about conversions among classes related by inheritance:
* The conversion from derived to base applies only to the pointer or reference types.
* There is no implicit conversion from the base-class type to the derived type.
* Like any member, the derived-to-base conversion may be inaccessible due to access controls.
When a virtual function is called through a reference or pointer, the compiler generates code to decide at run time which function to call.
A function that is virtual in a base class is implicitly `virtual` in its derived classes. When a derived class overrides a virtual, the parameters in the base and derived classes must match exactly.
It is legal for a derived class to define a function with the same name as a virtual in its base class but with a different parameter list. The compiler considers such a function to be independent of the base-class function. In such cases. the derived version does not override the version in the base class.
Finding such bugs can be surprisingly hard. Under the new standard, we can specify override on a virtual function in a derived class. The compiler will reject a program if a function marked override does not override an existing virtual function:
```cpp
struct B {
virtual void f1(int) const;
virtual void f2();
void f3();
};
struct D1 : B {
void f1(int) const override; // ok, f1 matches f1 in the base
void f2(int) override; // error: B has no f2(int) function
void f3() override; // error: f3 is virtual function
void f4() override; // error: B doesn't have a function named f4
};
```
Any attempt to override a function that has been defined as final will be flagged as an error:
```cpp
struct D2 : B {
void f1(int) const final; // subsequent classes can't override f1(int)
};
struct D3 : D2 {
void f2(); // ok: override f2 inherited from the indirect base B
void f1(int) const; // error: D2 declared f2 as final
};
```
When a call is made through a reference or pointer to base, the default argument(s) will be those defined in the base class. The base-clas arguments will be used even when the derived version of the function is run.
Virtual functions that have default arguments should use the same argument values in the base and derived classes.
```cpp
double undiscounted = baseP->Quote::net_price(42);
```
## 15.4 Abstract Base Classes
A pure virtual function. Unlike ordinary virtuals, a pure virtual function does not have to be defined.
A class containing(or inheriting without overriding) a pure virtual function is an abstract base class.
We may not create objects of a type that is an abstract base class.
```cpp
class Bulk_quote : public Disc_quote {
Bulk_quote(const std::string& book, double price,
std::size_t qty, double disc) :
Disc_quote(book, price, qty, disc) {}
};
```
## 15.5 Access Control and Inheritance
A class uses protected for those members that it is willing to share with its derived classes but wants to protect from general access.
* protected members are inaccessible to users of the class.
* protected members are accessible to members and friends of classes derived from this class.
* A derived class member or friend may access the protected members of the base class only through a derived object. The derived class has no special access to the protected members of base-class objects.
```cpp
class Base {
protected:
int prot_mem;
};
class Sneaky : public Base {
friend void clobber(Sneaky&); // can access Sneaky::prot_mem
friend void clobber(Base&); // can't access Base::prot_mem
}
```
For any given point in your code, if a public member of the base class would be accessible, then the derived-to-base conversion is also accessible, and not otherwise.
Friendship is not inherited; each class controls access to its members.
Sometimes we need to change the access level of a name that a derived class inherits. We can do so by providing a using declaration:
```cpp
class Base {
public:
std::size_t size() const { return n; }
};
class Derived : private Base {
public:
// maintain access levels for members related to the size of the object
using Base::size;
protected:
using Base::n;
};
```
A using declaration inside a class can name any accessible member of a direct or indirect base class.
A derived class may provide a using declaration only for names it is permitted to access.
By default, a derived class defined with the class keyword has private inheritance; a derived class defined with struct has public inheritance:
```cpp
class Base { /* ... */ }
struct D1 : Base { /* ... */ } // public inheritance by default
class D2 : Base { /* ... */ } // private inheritance by dfault
```
## 15.6 Class Scope under Inheritance
The scope of a derived class nest inside the scope of its base classes.
A derived-class member with the same name as a member of the base class hides direct use of the base-class member.
We can use a hidden base-class member any by using the scope operator:
```cpp
struct Derived : Base {
int get_base_mem() { return Base::mem; }
};
```
Functions declared in an inner scope do not overload functions declared in an outer scope:
```cpp
struct Base {
int memfcn();
};
struct Derived : Base {
int memfcn(int); // hides memfcn in the base
};
Derived d; Base b;
b.memfcn(); // calls Base::memfcn;
d.memfcn(10); // calls Derived::memfcn;
d.memfcn(); // error: memfcn with no arguments is hidden
d.Base::memfcn; // ok: calls Base::memfcn
```
If the base and derived members took arguments that differed from one another, there would be no way to class the derived version through a reference or pointer to the base class.
## 15.7 Constructors and Copy Control
### 15.7.1 Virtual Destructors
The primary direct impact that inheritance has on copy control for a base class is that a base class generally should define a virtual destructor. The destructor needs to be virtual to allow objects int the inheritance hierarchy to be dynamically allocated.
Executing delete on a pointer to base that points to a derived object has undefined behaviour it the base's destructor is not virtual.
If a class defines a destructor - even if it uses `= default` to use the synthesized version - the compiler will not synthesize a move operation for that class.
### 15.7.2 Synthesized Copy Control and Inheritance
These synthesized members initialize, assign, or destroy the direct base part of an object by using the corresponding operation from the base class.
* If the default constructor, copy constructor, copy-assignment operator, or destructor in the base class is deleted or inaccessible, then the corresponding member in the derived class is defined as deleted, because the compiler can't use the base-class member to construct, assign, or destroy the base-class part of the object.
* If the base class has an inaccessible or deleted destructor, then the synthesized default and copy constructors int eh derived classes are defined as deleted, because there is no way to destroy the base part of the derived object.
* As usual, the compiler will not synthesize a deleted move operation. If we use = default to request a move operation, it will be a deleted function in the derived if the corresponding operation in the base is deleted or inaccessible, because the base class part cannot be moved. The move constructor will also be deleted if the base class destructor is deleted or inaccessible.
Most base classes define a virtual destructor. As a result, by default, base classes generally do not get synthesized move operations. Classes derived from a base class that doesn't have move operations don't get synthesized move operations either.
### 15.7.3 Derived-Class Copy-Control Members
When a derived class defines a copy or move operation, that operation is responsible for copying or moving the entire object, including base-class members.
When we define a copy or move constructor for a derive3d class, we ordinarily use the corresponding base-class constructor to initialize the base part of the object:
```cpp
D(const D& d) : Base(d) // copy the base members
D(D&& d) : Base(std::move(d)) // move the base members
```
`Base(d)` will match the `Base` copy constructor. The `D` object, d, will be bound to the `Base&` parameter in the constructor.
A derived-class assignment operator must assign its base part explicitly:
```cpp
// Base::operator=(const Base&) is not invoked automatically
D &D::operator=(const D &rhs) {
Base::operator=(rhs); // assigns the base part
// assign the members in the derived class, as usual
// handling self-assignment and freeing existing resource as appropriate
return *this;
};
```
The base-class parts of an object are also implicitly destroyed. As a result, unlike the constructors and assignment operators, a derived destructor is responsible only for destroying the resources allocated by the derived class.
The base-class part of a derived object is constructed first. While the base-class constructor is executing, the derived part of the object is uninitialized. Similarly, derived objects are destroyed in reverse order, so that when a base class destructor runs, the derived part has already been destroyed.
### 15.7.4 Inherited Constructors
Under the new standard, a derived class can reuse the constructors defined by its direct base class.
```cpp
class Bulk_quote : public Disc_quote {
using Disc_quote::Disc_quote; // inherit Disc_quote's constructors
};
```
Unlike `using` declarations for ordinary members, a constructor `using` declaration does not change the access level of the inherited constructor.
## 15.8 Containers and Inheritance
Because derived objects are "sliced down" when assigned to a base-type object, containers and types related by inheritance do not mix well.
When we need a container that holds objects related by inheritance, we typically define the container to hold pointers to the base class.
- Introduction
- Chapter 1. Getting Start
- Chapter 2. Variables and Basic Types
- Chapter 3. String, Vectors, and Arrays
- Chapter 4. Expressions
- Chapter 5. Statements
- Chapter 6. Functions
- Chapter 7. Classes
- Chapter 8. The IO Library
- Chapter 9. Sequential Containers
- Chapter 10. Generic Algorithms
- Chapter 11. Associative Container
- Chapter 12. Dynamic Memory
- Chapter 13. Copy Control
- Chapter 14. Overloaded Operations and Conversions
- Chapter 15. Object-Oriented Programming
- Chapter 16. Template Argument Deduction
- Chapter 17. Specialized Library Facilities
- Chapter 18. Tools for Large Programs
- Chapter 19. Specialized Tools and Techniques
