Use of delete in an abstract class pointer

Asked

Viewed 38 times

4

Reading the book A tour of C++, in a section on memory leakage, when I came across 3 statements regarding the code below:

  1. The implementation of Smiley may fail to delete pointer to mouth.
  2. The attempt to delete the returned pointer from read_shape() can fail.
  3. A variable of the type Shape* may fail to attempt to delete the object it points to.

Original text:

Experienced programmers will have noticed that I left open three Opportunities for mistakes:
• The implementer of Smiley may fail to delete the Pointer to Mouth.
• A user of read_shape() Might fail to delete the Pointer returned.
• The Owner of a container of Shape pointers Might fail to delete the Objects pointed to.

THE CODE BELOW DOES NOT COMPILE, IT IS JUST A REPRESENTATION OF THE BOOK TO UNDERSTAND THE PROBLEM.

class Shape
{
    public:
        virtual ~Shape() {}
};

class Circle : public Shape
{
    public:
        Circle(Point _center, int _rad) : center { _center }, rad { _rad }
        {}

    private:
        Point center;
        int rad;
};

class Smiley : public Circle
{
    public:
        Smiley(Point center, int rad) : Circle { center, rad }, mouth { nullptr }
        {}

        ~Smiley()
        {
            delete mouth; // Referencia da primeira afirmação.
            for (auto eye : eyes)
                delete eye;
        }

        void set_mouth(Shape* _mouth)
        {
            mouth = _mouth;
        }

    private:
        std::vector<Shape*> eyes;
        Shape* mouth;

};

Shape* read_shape(std::istream& is)
{
    //Read kind_of_shape

    switch (kind_of_shape)
    {
        case Circle:
            //Read p, r.
            return new Circle { p, r };
            break;

        case Triangle:
            //Read p1, p2, p3.
            return new Triangle { p1, p2, p3 };
            break;

        case Smiley:
            //Read {Point, int} int p, r.
            Smiley∗ ps = new Smiley{p,r};
            return ps;
            break;
    }
}

I realized that the problem of implementation was then in the Shape*, I mean, a pointer to an abstract class, but I don’t understand what the problem is. The text still describes:

Pointers to objects allocated in free store¹ are dangerous: an old Pointer' ² should not be used to represent a property.

Original text:

... pointers to Objects allocated on the free store is Dangerous: a ːː Plain old Pointer'' should not be used to represent Ownership.

The question that remains is: Because the command delete in a type variable Shape* maybe I’ll fail? .


[1][2] I don’t know a proper translation.


The solution to this problem presented by the text is using std::unique_ptr<Shape>() in place of Shape*.

1 answer

2


I realized that the implementation problem was then in Shape*, that is, a pointer to an abstract class, but I didn’t understand what the problem was

The problem is not in Shape nor the fact that it is abstract. Problems appear in read_shape() because it returns a pointer, and in Smiley because one of the properties is a pointer.

They are loose ends and out of control. That’s the theme: "Avoiding Resource Leaks" in 4.5.3: Preventing leaks.

Case 1

One of the properties of Smiley is a pointer to mouth, initialized with nullptr in the default constructor. This is a risk.

The question is: Why the delete command on a Shape* type variable might fail?

That is not what Prof. Stroustrup wrote. He wrote:

"The implementer of Smiley may fail to delete the Pointer to mouth"

something like "the implementor of Smiley may fail to erase the pointer to mouth". And if it fails there is already. The author did not speak of the possibility of the command delete fail.

Case 2

Shape* read_shape(std::istream& is)

Whoever calls it gets one Shape* and a responsibility. It’s more of a loose end --- Loose-end --- and if the guy didn’t release it was already.

Case 3

class Smiley : public Circle
{
    public:
        Smiley(Point center, int rad) : Circle { center, rad }, mouth { nullptr }
        {}

        ~Smiley()
        {
            delete mouth; // Referencia da primeira afirmação.
            for (auto eye : eyes)
                delete eye;
        }

        void set_mouth(Shape* _mouth)
        {
            mouth = _mouth;
        }

    private:
        std::vector<Shape*> eyes;
        Shape* mouth;

};

See the destructor code of Smyley above: first solve case 1 and then case 3.

A Smiley Spider type will have 8 eyes. And if it only erases 7?

    private:
        std::vector<Shape*> eyes; // usually two eyes
        Shape* mouth;

That’s the problem. Who wrote Shape or Circle is not to blame. Shape is abstract and has no problem, until someone derives in this way.

Anyway, responsibility wasn’t supposed to be a problem. It’s life.
But the idea at this point is to introduce unique_ptr<> as a solution not to leave these things open.

And reinforce the fact that in a class-derived hierarchy constructors are called "from the outside in" and destructors are called the other way around. So in creating Smiley first calf Shape afterward Circle afterward Smiley. But by destroying Smiley the order is reverse.

  • So in the first case, I made the confusion because I could not translate the text the right way?

  • And why "Smiley implementer might fail to erase pointer to Mouth" if Smiley destructor invokes delete Mouth? Smiley’s destroyer shouldn’t be called once he’s out of the scope?

  • "See the Shape destructor code: first solve case 1 and then case 3." Shape’s destructor is empty, as it solves something?

  • 1

    That’s the point: Shape created no problem. Smiley introduced a vulnerability. It’s not the end of the world. And that’s why destructors begin to be invoked by the inner class. And the author talks about hypotheses. "If" the implementor fails. In this example it did not fail. And I misspelled: I was referring to the destructor of Smiley that does the expected and in the right order. Sorry. I will correct

Browser other questions tagged

You are not signed in. Login or sign up in order to post.