Search code examples
c++virtualassignment-operator

C++ virtual assignment operator


I am trying to call the assignment operator from the base class (shape) in the derived class (point). I get an unresolved external linker error and I don't understand why. I put "virtual" in front of the base assignment operator and placed the base assignment operator inside of my derived class at the end. Where did I go wrong?

#ifndef SHAPE_H
#define SHAPE_H
#include <iostream>

using namespace std;

namespace Joe
{
    namespace CAD
    {
        class Shape
        {
        private:
            int m_id;
        public:
            Shape();    //default constructor
            ~Shape();    //destructor
            Shape(const Shape& s);    //copy constructor
            virtual Shape& operator = (const Shape& source);    //assignment operator
            string ToString() const;    //returns id as a string
            friend ostream& operator << (ostream& os, const Shape& sh);    //global function that can access private members
            int ID() const;
        };

        inline int Shape::ID() const    //retrieve ID of shape
            {
                return m_id;
            }
    }
}
#endif

#ifndef POINT_H
#define POINT_H
#include <iostream>
#include "shape.h"

namespace Joe
{
    namespace CAD
    {
        class Point: public Shape
        {
        private:
            double m_x;
            double m_y;
        public:
            Point();    //default constructor
            ~Point();    //destructor
            Point(const Point& pt);    //copy constructor
            Point(double newX, double newY)    //constructor accepting x and y coordinates
            {
                m_x = newX;
                m_y = newY;
                std::cout << "Point(" << m_x <<","<< m_y <<")" << std::endl;
            }

            Point(double val);    //constructor accepting one double

            void X(double newXval) {m_x = newXval;}    //default inline setter
            void Y(double newYval) {m_y = newYval;}    //default inline setter

            double X() const;    //getter pre-inline
            double Y() const;    //getter pre-inline


            std::string ToString() const;    //returns a string description

            //distance functions
            double Distance() const;    //calculate the distance to the origin (0,0)
            double Distance(const Point& p) const;    //calculate the distance between two points

            //operator overloading
            Point operator - () const;    //negate the coordinates
            Point operator * (double factor) const;    //scale the coordinates
            Point operator + (const Point& p) const;    //add coordinates
            bool operator == (const Point& p) const;    //equally compare operator
            Point& operator = (const Point& source);    //assignment operator
            Point& operator *= (double factor);    //scale the coordinates and assign

            friend std::ostream& operator << (std::ostream& os, const Point& p);    //send to ostream (friend)
            Shape& operator = (const Shape& source);    // call assignment operator of base class
        };

        inline double Point::X() const    //normal inline getter
        {
            return m_x;
        }

        inline double Point::Y() const    //normal inline getter
        {
            return m_y;
        }
    }
}
#endif

Shape& Shape::operator = (const Shape& source)    //assignment operator
        {
            if (this == &source)    //avoid self-assignment
                return *this;
            cout << "shape assignment" << endl;
            m_id = source.m_id;
            return *this;
        }

Point& Point::operator = (const Point& source)    //assign
        {
            if (this == &source)    //avoid self-assignment
                return *this;
            m_x = source.m_x;
            m_y = source.m_y;
            return *this;
        }

Solution

  • Your problem might be that you define the class in a namespace, but you define the operators outside that namespace without specifying it. Because of this, the compiler can't connect the definition to the declaration.

    Ishamael pointed out that you don't specify what happens when a non-Point Shape is assigned to a Point. This is necessary for a virtual assignment operator; see his answer. But this virtual assignment operator could end up doing a lot of unexpected things, like cutting off parts of objects if the wrong types are assigned to each other.

    You don't need the virtualness to ensure that the Shape operator also gets called when Point is assigned. Just call the Shape operator inside the Point operator. Making the operator virtual would actually have the opposite effect; the Point operator would override the Shape operator even if the instance you're calling it with is referred to as a Shape.

    Point& Point::operator = (const Point& source)    //assign
        {
            if (this == &source)    //avoid self-assignment
                return *this;
            Shape::operator=(source);  // also call the base class operator
            m_x = source.m_x;
            m_y = source.m_y;
            return *this;
        }