Let's assume we have a very basic class A:
class A {
    public:
        void SetName(const std::string& newName) {
            m_name=newName;
        }
        void Print() const {
            std::printf("A::Print(). Name: %s\n",m_name.c_str());
        }
    private:
        std::string m_name;  
};
We want to extend this class with class B so we add our virtual destructor, change a member to virtual and change private to protected for inh:
class A {
    public:
        virtual ~A() {}
        void SetName(const std::string& newName) {
            m_name=newName;
        }
        virtual void Print() const {
            std::printf("A::Print(). Name: %s\n",m_name.c_str());
        }
    protected:
        std::string m_name;
};
class B : public A {
    public:
        virtual void Print() const {
            std::printf("B::Print(). Name: %s\n",m_name.c_str());
        }
};
Now since we added a destructor in class A  do we need to create a copy constructor and copy operator like so?
class A {
    public:
        virtual ~A() {}
        A() = default;
        A(const A& copyFrom){
            *this = copyFrom;
        }
        virtual A& operator=(const A& copyFrom){
            m_name=copyFrom.m_name;
            return *this;
        };
        void SetName(const std::string& newName) {
            m_name=newName;
        }
        virtual void Print() const {
            std::printf("A::Print(). Name: %s\n",m_name.c_str());
        }
    protected:
        std::string m_name;
};
To me this seems unnecessary as the default copy operator and copy constructor would do the same thing.