как деструктор работает в с ++ - PullRequest
4 голосов
/ 23 января 2012

вот мой код на C ++:

  class Sample
    {
    public:
            int *ptr;
            Sample(int i)
            {
            ptr = new int(i);
            }
            ~Sample()
            {
            delete ptr;
            }
            void PrintVal()
            {
            cout << "The value is " << *ptr;
            }
    };
    void SomeFunc(Sample x)
    {
    cout << "Say i am in someFunc " << endl;
    }
    int main()
    {
    Sample s1= 10;
    SomeFunc(s1);
    s1.PrintVal();
    }

он возвращает мне вывод вроде:

Say i am in someFunc 
Null pointer assignment(Run-time error)

здесь Когда объект передается по значению SomeFunc, деструктор объектавызывается, когда управление возвращается из функции

Должен ли я прав?если да, то почему это происходит?и какое решение для этого ???

Ответы [ 5 ]

5 голосов
/ 23 января 2012

Sample передается по значению SomeFunc, что означает, что копия сделана.Копия имеет тот же ptr, поэтому, когда эта копия уничтожается при возврате SomeFunc, ptr удаляется для обоих объектов.Затем, когда вы вызываете PrintVal() в основном, вы разыменовываете этот неверный указатель.Это неопределенное поведение.Даже если это работает, тогда, когда s1 уничтожается, ptr снова удаляется, что также является UB.

Кроме того, если компилятору не удается удалить копию в Sample s1= 10;, s1 не будетдаже быть действительным для начала, потому что, когда временный уничтожен, указатель будет удален.Однако большинство компиляторов избегают этого копирования.

Вам необходимо либо правильно выполнить копирование, либо запретить копирование.Copy-ctor по умолчанию не подходит для этого типа.Я бы рекомендовал либо сделать этот тип типом значения (который содержит его члены напрямую, а не по указателю), чтобы работал copy-ctor по умолчанию, или использовать умный указатель для хранения ссылки, чтобы он мог управлять ресурсами для ссылки по ссылке.вы и copy-ctor по умолчанию все еще будете работать.

Одна из вещей, которые мне действительно нравятся в C ++, это то, что он действительно удобен для повсеместного использования типов значений, и если вам нужен ссылочный тип, вы можете просто обернуть любое значениевведите в умный указатель.Я думаю, что это намного лучше, чем в других языках, в которых есть примитивные типы с семантикой значений, но тогда пользовательские типы по умолчанию имеют семантику ссылок.

5 голосов
/ 23 января 2012

Обычно вам нужно подчиняться правилу трех , так как у вас есть член-указатель.
В вашем примере кода, чтобы избежать неопределенного поведения, которое вы видите:

Заменить необходимо в первом утверждении на must .

1 голос
/ 23 января 2012

Поскольку SomeFunc() принимает свой аргумент по значению, объект Sample, который вы передаете ему, копируется.Когда возвращается SomeFunc(), временная копия уничтожается.

Поскольку для Sample не определен конструктор копирования, его созданный компилятором конструктор копирования просто копирует значение указателя, поэтому оба экземпляра Sample указывают на одно и то жеint.Когда один Sample (временная копия) уничтожается, этот int удаляется, а затем, когда второй Sample (оригинал) уничтожается, он пытается удалить тот же самый int снова.Вот почему ваша программа аварийно завершает работу.

Вы можете изменить SomeFunc() на взятие ссылки, избегая временной копии:

void someFunc(Sample const &x)

и / или вы можете определить конструктор копирования для Sample который выделяет новый int, а не просто копирует указатель на существующий.

0 голосов
/ 23 января 2012

Вместо

int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
}

попробуйте использовать

int main()
{
Sample* s1= new Sample(10);
SomeFunc(*s1);
s1->PrintVal();
}
0 голосов
/ 23 января 2012

Когда вы передаете аргумент для функции, он называется конструктором копирования, но у вас его нет, поэтому указатель не инициализируется. Когда он выходит из функции, объект вызывает деструктор, чтобы удалить унитализированный указатель, поэтому он выдает ошибку.

...