Nie wiem na czym polegało zadanie, ani co książka mówi na ten temat, ale:
Dodanie virtual w twoim kodzie nic nie zmienia (poza tym, że kompilator ma troche więcej roboty i tworzy się więcej kodu maszynowego). Zauważ, że w obu klasach operatory przypisania wyglądają nieco inaczej. Oba zwracają i przyjmują referencje na swój typ (dziwnie to brzmi, ale myśle, że wiadomo o co chodzi). Cały mechanizm nadpisywania metod zadziała jeśli deklaracja metod w obu klasach (bazowej i pochodnej) nie różni się. Tutaj masz po prostu dwie różne metody. I tak - Classic ma dwa operatory przypisania. Jeden (odziedziczony) zadziała dla obiektów Cd, a drugi dla obiektów Classic (pierwszy wariant też by "pasował", ale kompilator wybiera bardziej restrykcyjny wariant). W uproszczeniu, bo Classic::operator=(const Cd &) uruchomi się też gdy dostanie referencje na Cd wskazującą jednak na obiekt Classic.
Także koniec końców virtual tutaj mija się z celem. Rozwiązaniem byłoby oczywiście zamienienie operatora z Classic na taki sam jak w klasie bazowej - wtedy overriding zadziała. Tylko, że to cię ogranicza (operator nie wie jakiego typu obiekt dostał). To możesz obejść testując typ dynamic_castem, ale to z kolei jest czasochłonne (bo RTTI). Tutaj już dość indywidualna decyzja, ale nie wygląda to najpiękniej. Szczerze mówiąc nie wiem jak się rozwiązuje ten problem, ale wydaje mi się, że po prostu się tego nie robi (nigdzie jeszcze nie widziałem wirtualnego operatora przypisania i sam też nigdy nie miałem potrzeby tworzyć takiego).
Mam nadzieję, że nie zagmatwałem (wydaje mi się, że jednak tak :D). Jeśli coś jest niejasne, chętnie wytłumacze :)