Возврат переменной, созданной внутри l oop, приводит к двойному вызову деструктора - PullRequest
1 голос
/ 26 апреля 2020

Я пытаюсь понять, что стандарт C ++ говорит о том, как / когда должен вызываться деструктор, когда объект возвращается из функции - рассмотрим эту простую структуру и две функции -

#include <iostream>
int g = 0;
struct foo {
    int myid;
    foo() {
        myid = g;
        g++;
        std::cout << "Created " << myid << std::endl;
    }
    ~foo() {
        std::cout << "Destroyed " << myid << std::endl;
    }
};

foo bar(void) {
    int i = 0;
    for (foo s; i < 10; i++) {
        if (i == 5)
            return s;
    }
}
foo bar2(void) {
    int i = 0;
    foo s;
    for (; i < 10; i++) {
        if (i == 5)
            return s;
    }
}

int main() {
    bar();
    bar2();
    return 0;
}

I Я пытаюсь отследить, сколько раз вызывается деструктор. Вывод из вышеуказанной программы -

Created 0
Destroyed 0
Destroyed 0
Created 1
Destroyed 1

Я могу понять поведение bar2. Объект создается один раз и уничтожается (я считаю, деструктор вызывается из main). Но в bar когда объект объявлен внутри l oop. Это случай, когда деструктор будет вызван дважды. В чем причина этого несоответствия?

Это тот случай, когда стандарт оставляет это поведение для реализации (из-за исключения копирования?), А g ++ просто выбирает это поведение для двух случаев? Если так, как я могу написать эту функцию, чтобы получить предсказуемое поведение. Мне нужно, чтобы деструктор вызывался ровно столько же раз, сколько конструктор (и предпочтительно в обратном порядке). Я в порядке с деструктором, вызываемым вдвое, если конструктор вызывается также дважды. Причина в том, что я размещаю некоторые данные внутри конструктора и освобождаю их внутри деструктора.

Ответы [ 2 ]

2 голосов
/ 26 апреля 2020

Добавить этот код

foo(const foo& rhs) {
    myid = g;
    g++;
    std::cout << "Created from copy " << myid << std::endl;
}

Это конструктор копирования, он также вызывается, только вы не знали об этом, потому что вы использовали версию по умолчанию, которая, очевидно, ничего не печатает или увеличьте счетчик.

0 голосов
/ 26 апреля 2020

cppinsights сообщает вам, что происходит: вызывается конструктор копирования по умолчанию, поэтому также уничтожается копия.

Там, однако, оба объекта подлежат именованному возвращаемому значению Оптимизация, вариант copy elision , который исключает конструктор копирования. Если вы компилируете и запускаете свой код с помощью clang, это действительно так (https://godbolt.org/z/KWhRpL не имеет двойного «Уничтожено»).

NRVO не является обязательным, и кажется, что г cc не применяется там. Нет способа заставить NRVO произойти, но вы могли бы реализовать конструктор перемещения, который будет вызываться вместо этого.

...