Recommand · June 1, 2021 0

Expected linking error in C++ but executable builds fine

I have pasted the code below. I was expecting lines Circle {p, radius} and Circle::draw() to cause linking error because Circle constructor as well as the draw functions are merely declarations without definitions. However, the executable gets created properly without any linking errors. Why is there no linking error here?

#include<iostream>
#include<vector>
using namespace std;

class Point {
    public:
        double x() {
            return x_coordinate;
        }
        double y() {
            return y_coordinate;
        }
    private:
        double x_coordinate, y_coordinate;
};

class Shape {
    public:
        virtual Point center() const =0;
        virtual void move(Point to) =0;
        virtual void draw() const =0;
        virtual void rotate(int angle) =0;
        virtual ~Shape() {}
};

class Circle: public Shape {
    public:
        Circle(Point p, int radius);
        Point center() {
            return c;
        }
        void move(Point to) {
            c = to;
        }
        void draw() const override; 
        void rotate(int angle) override {}
    private:
        Point c;    
        int r;
};

class Smiley: public Circle {
    public:
        Smiley(Point p, int radius):
            Circle {p, radius}, mouth {nullptr} {}

        ~Smiley() {
            delete mouth;
            for(auto eye: eyes) {
                delete eye;
            }
        }

        void move(Point to) override;
        void draw() const override {
            Circle::draw();
            for(auto e: eyes) {
                e->draw();
            }
            mouth->draw();
        }
        void rotate(int angle) override;
        void add_eye(Shape *eye) {
            eyes.push_back(eye);
        }
        void set_mouth(Shape *s);
        virtual void wink(int i);
    private:
        vector<Shape *>eyes;
        Shape *mouth;
};

void rotate_all(vector<Shape*>& v, int angle) {
    for(auto p : v) {
        p->rotate(angle);
    }
}

int main() {
    return 0;
}

There are no linker errors because:

int main() {
    return 0;
}

Your program does not need to link to any method other than main to work, and main doesn’t do anything.

The constructor Circle(Point, int) is never called except from inline functions. Since those inline functions are never called, the code for them is not emitted, and you get no link error.

If you call one of these functions from a non-inline function, you will get an error (with a typical toolchain–this may depend on whether you do things like enable LTO). For example, make this change:

Redeclare the Smiley constructor as non-inline:

Smiley(Point p, int radius);

Provide a definition outside the class (remember that member functions defined inside the class body are inline by default):

Smiley::Smiley(Point p, int radius):
    Circle {p, radius}, mouth {nullptr} {}

You will now get a link error. This is the error I get:

$ c++ test.cpp 
/usr/bin/ld: /tmp/ccw9y6G0.o: warning: relocation against `_ZTV6Smiley' in read-only section `.text'
/usr/bin/ld: /tmp/ccw9y6G0.o: in function `Smiley::Smiley(Point, int)':
test.cpp:(.text+0x4d): undefined reference to `Circle::Circle(Point, int)'
/usr/bin/ld: test.cpp:(.text+0x54): undefined reference to `vtable for Smiley'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
[Exit: 1]