From d3dda0311a9ab5ba72ee4ba102bec6b1567736ea Mon Sep 17 00:00:00 2001
From: Marcus Mohr <marcus.mohr@lmu.de>
Date: Wed, 15 Dec 2021 16:13:42 +0100
Subject: [PATCH] Adds sections on override, final and pure virtual functions

---
 notebooks/10_Polymorphic_Classes.ipynb | 205 +++++++++++++++++++++++--
 1 file changed, 194 insertions(+), 11 deletions(-)

diff --git a/notebooks/10_Polymorphic_Classes.ipynb b/notebooks/10_Polymorphic_Classes.ipynb
index df655b1..9cd6044 100644
--- a/notebooks/10_Polymorphic_Classes.ipynb
+++ b/notebooks/10_Polymorphic_Classes.ipynb
@@ -13,7 +13,7 @@
    "id": "7ec80016",
    "metadata": {},
    "source": [
-    "We return to our example from [Go19] and extend it a little."
+    "We return to our example from <a href=\"https://www.hanser-kundencenter.de/fachbuch/artikel/9783446458468\">[Go19]</a> and extend it a little."
    ]
   },
   {
@@ -41,7 +41,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 2,
    "id": "f40024e2",
    "metadata": {},
    "outputs": [],
@@ -64,7 +64,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 3,
    "id": "459a1ce0",
    "metadata": {},
    "outputs": [],
@@ -95,7 +95,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 4,
    "id": "5ff4684f",
    "metadata": {},
    "outputs": [],
@@ -127,7 +127,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 5,
    "id": "aebeaf35",
    "metadata": {},
    "outputs": [
@@ -156,7 +156,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 6,
    "id": "445c4d28",
    "metadata": {},
    "outputs": [
@@ -187,7 +187,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 7,
    "id": "4b596bd2",
    "metadata": {},
    "outputs": [],
@@ -240,7 +240,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 8,
    "id": "12bebbf9",
    "metadata": {},
    "outputs": [
@@ -302,7 +302,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 9,
    "id": "ccb162c8",
    "metadata": {
     "scrolled": true
@@ -378,7 +378,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 10,
    "id": "99352379",
    "metadata": {},
    "outputs": [],
@@ -407,10 +407,193 @@
     "secretary->all_info();"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "80e7e3b3",
+   "metadata": {},
+   "source": [
+    "Especially in larger code bases it can be come tedious to not loose the overview on which member functions are virtual and which are not. Or whether we are overriding the correct version of a method."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "bd457522",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#include <iostream>\n",
+    "\n",
+    "struct A {\n",
+    "    void func1() { std::cout << \"A::func1 is executing\" << std::endl; };\n",
+    "    virtual void func2( double a ) { std::cout << \"func2 from A: a = \" << a << std::endl; }; \n",
+    "};\n",
+    "\n",
+    "\n",
+    "struct B : public A {\n",
+    "    void func1() { std::cout << \"B::func1 is executing\" << std::endl; };\n",
+    "    virtual void func2( int a ) { std::cout << \"func2 from B: a = \" << a << std::endl; };\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "id": "4a0a85ba",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "A::func1 is executing\n",
+      "func2 from A: a = 2\n"
+     ]
+    }
+   ],
+   "source": [
+    "A* obj = new B;\n",
+    "obj->func1();\n",
+    "obj->func2( 2 );"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6414fc0e",
+   "metadata": {},
+   "source": [
+    "`A::func1` is not virtual, so we don't overide and while `A::func2` is virtual, the interface of `B::func2` is different.\n",
+    "\n",
+    "A smart compiler might warn us on the latter:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "3c824171",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Writing fail.cpp\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%file fail.cpp\n",
+    "\n",
+    "#include <iostream>\n",
+    "\n",
+    "struct A {\n",
+    "  void func1() { std::cout << \"A::func1 is executing\" << std::endl; };\n",
+    "    \n",
+    "  virtual void func2( double a ) { std::cout << \"func2 from A: a = \" << a\n",
+    "                                             << std::endl; }; \n",
+    "};\n",
+    "\n",
+    "\n",
+    "struct B : public A {\n",
+    "  void func1() { std::cout << \"B::func1 is executing\" << std::endl; };\n",
+    "  virtual void func2( int a ) { std::cout << \"func2 from B: a = \" << a\n",
+    "                                          << std::endl; };\n",
+    "};"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "29c729c5",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "fail.cpp:14:16: warning: 'B::func2' hides overloaded virtual function [-Woverloaded-virtual]\n",
+      "  virtual void func2( int a ) { std::cout << \"func2 from B: a = \" << a\n",
+      "               ^\n",
+      "fail.cpp:7:16: note: hidden overloaded virtual function 'A::func2' declared here: type mismatch at 1st parameter ('double' vs 'int')\n",
+      "  virtual void func2( double a ) { std::cout << \"func2 from A: a = \" << a\n",
+      "               ^\n",
+      "1 warning generated.\n"
+     ]
+    }
+   ],
+   "source": [
+    "!clang++ -Wall -Wextra -c fail.cpp"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6774e9d0",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  },
+  {
+   "cell_type": "markdown",
+   "id": "413b3e71",
+   "metadata": {},
+   "source": [
+    "### Pure Virtual Functions and Abstract Classes"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ebb14ead",
+   "metadata": {},
+   "source": [
+    "Consider the following application scenario:\n",
+    "\n",
+    "- We want to implement four specialised matrix classes\n",
+    "  - `DenseMatrix`\n",
+    "  - `DenseSymMatrix`\n",
+    "  - `SparseMatrix`\n",
+    "  - `SparseSymMatrix`\n",
+    "- They all should be children of a common `BaseMatrix` base class.\n",
+    "- All of them should provide a method `multWithVector()` with the same signature.\n",
+    "- We want to be able to invoke that method on a base class pointer or reference.\n",
+    "\n",
+    " This gives rise to the following questions:\n",
+    " 1. How should `BaseMatrix::multWithVector()` be implemented?\n",
+    " 1. Can we enforce that someone who adds another child, say `DenseSkewMatrix` does not forget to implement `DenseSkewMatrix::multWithVector()`?\n",
+    "\n",
+    "A sensible implementation of `BaseMatrix::multWithVector()` will not be possible, as the base class has no knowledge on the details of how its children store their matrices. We could only do an empty implementation or one that e.g. throws an exception. The latter would at least lead to a runtime crash, if we call `multWithVecotr()` on a base class pointer or reference that targets a `DenseSkewMatrix`.\n",
+    "\n",
+    "The preferred solution, however, would be to make `BaseMatrix::multWithVector()` a **pure virtual function**."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "id": "3069e8e2",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class Vector;\n",
+    "\n",
+    "class BaseMatrix {\n",
+    "    virtual void multWithVector( const Vector& src, Vector& dst ) const = 0;\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ec47a18b",
+   "metadata": {},
+   "source": [
+    "* A pure virtual function has no implementation in the class where it is declared. It only describes an **interface**.\n",
+    "* Adding a pure virtual function to a class makes it an **abstract class**, i.e. we cannot instantiate an object of that class.\n",
+    "* The same holds for its children (via inheritance), as long as they do not implement the method."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "9f63ea43",
+   "id": "24c9d364",
    "metadata": {},
    "outputs": [],
    "source": []
-- 
GitLab