Как обрабатывать динамически размещенную переменную в классе? - PullRequest
0 голосов
/ 24 декабря 2011

Я пытаюсь создать класс, который содержит динамически создаваемую переменную.Мой класс в настоящее время выглядит примерно так:

class Foo
{
    private:
    int *dynamic_int;
    public:
    Foo ()
    {
        dynamic_int = new int;
    }
    ~Foo ()
    {
        delete dynamic_int;
    }
};

Создание экземпляра класса не вызывает проблем.Следующий скрипт работает как charm:

int main ()
{
    Foo a;
    return 0;
}

Однако копирование переменной вызывает сбой программы:

int main ()
{
    Foo a;
    Foo b;
    b = a;
    return 0;
}

... возвращает ...

*** glibc detected *** ./tester: double free or corruption (fasttop): 0x000000000081a010 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3b5cc7c2d6]
./tester[0x40072b]
./tester[0x4006ca]
/lib64/libc.so.6(__libc_start_main+0xed)[0x3b5cc2169d]
./tester[0x4005c9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:03 15074305                           /home/jakob/Projects/c++ tester/tester
00600000-00601000 rw-p 00000000 08:03 15074305                           /home/jakob/Projects/c++ tester/tester
0081a000-0083b000 rw-p 00000000 00:00 0                                  [heap]
3b5c800000-3b5c822000 r-xp 00000000 08:01 132754                         /lib64/ld-2.14.90.so
3b5ca21000-3b5ca22000 r--p 00021000 08:01 132754                         /lib64/ld-2.14.90.so
3b5ca22000-3b5ca23000 rw-p 00022000 08:01 132754                         /lib64/ld-2.14.90.so
3b5ca23000-3b5ca24000 rw-p 00000000 00:00 0 
3b5cc00000-3b5cdab000 r-xp 00000000 08:01 136046                         /lib64/libc-2.14.90.so
3b5cdab000-3b5cfab000 ---p 001ab000 08:01 136046                         /lib64/libc-2.14.90.so
3b5cfab000-3b5cfaf000 r--p 001ab000 08:01 136046                         /lib64/libc-2.14.90.so
3b5cfaf000-3b5cfb1000 rw-p 001af000 08:01 136046                         /lib64/libc-2.14.90.so
3b5cfb1000-3b5cfb6000 rw-p 00000000 00:00 0 
3b5dc00000-3b5dc83000 r-xp 00000000 08:01 136047                         /lib64/libm-2.14.90.so
3b5dc83000-3b5de82000 ---p 00083000 08:01 136047                         /lib64/libm-2.14.90.so
3b5de82000-3b5de83000 r--p 00082000 08:01 136047                         /lib64/libm-2.14.90.so
3b5de83000-3b5de84000 rw-p 00083000 08:01 136047                         /lib64/libm-2.14.90.so
3b5e400000-3b5e415000 r-xp 00000000 08:01 162165                         /lib64/libgcc_s-4.6.2-20111027.so.1
3b5e415000-3b5e614000 ---p 00015000 08:01 162165                         /lib64/libgcc_s-4.6.2-20111027.so.1
3b5e614000-3b5e615000 rw-p 00014000 08:01 162165                         /lib64/libgcc_s-4.6.2-20111027.so.1
3b64400000-3b644e9000 r-xp 00000000 08:01 167743                         /usr/lib64/libstdc++.so.6.0.16
3b644e9000-3b646e8000 ---p 000e9000 08:01 167743                         /usr/lib64/libstdc++.so.6.0.16
3b646e8000-3b646f0000 r--p 000e8000 08:01 167743                         /usr/lib64/libstdc++.so.6.0.16
3b646f0000-3b646f2000 rw-p 000f0000 08:01 167743                         /usr/lib64/libstdc++.so.6.0.16
3b646f2000-3b64707000 rw-p 00000000 00:00 0 
7f94cd77e000-7f94cd783000 rw-p 00000000 00:00 0 
7f94cd79f000-7f94cd7a1000 rw-p 00000000 00:00 0 
7fffab7e7000-7fffab808000 rw-p 00000000 00:00 0                          [stack]
7fffab881000-7fffab882000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Сама проблема кажется мне достаточно ясной:
Когда присваивается значение a для b, сам экземпляр копируется, но указатель "dynamic_int" обоих экземпляров указывает на один и тот же адрес.В конце «основной» функции оба экземпляра уничтожаются.Это приводит к двойному удалению динамически размещаемой переменной.

Как я могу (и должен) справиться с этим?Что такое чистый способ справиться с этим?

Ответы [ 4 ]

3 голосов
/ 24 декабря 2011

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

Что вам нужно сделать, это выделить новый кусок памяти и скопировать в него значение a в вашем операторе присваивания и в конструкторе копирования.

1 голос
/ 24 декабря 2011

Компилятор C ++ автоматически сгенерирует конструктор копирования и оператор присваивания , если вы не определите его самостоятельно.

Итак, это последовательность событийкогда вы делаете b=a:

  • По умолчанию вызывается оператор присваивания , вызывающий побитовое мелкое копирование а в b.
  • Итак, a и b теперь указывают на один и тот же dynamic_int.
  • Когда вы нажимаете закрывающую скобку main(), оба a и b разрушаютсявызывая удаление, вызываемое в том же месте памяти, на которое указывает dynamic_int.

Как уже упоминалось в dasblinkenlight, вам нужно реализовать свой собственный конструктор копирования и оператор присваивания, если вы реализуете деструктор.

1 голос
/ 24 декабря 2011

Каждый класс C ++ имеет конструктор копирования , который вызывается, когда необходимо скопировать экземпляр этого класса.Аналогично, когда экземпляр назначается (как в вашем примере), вызывается оператор присваивания .

Если вы не определяете конструктор копирования или оператор присваивания явно, реализации по умолчаниюгенерируется компилятором, который просто копирует все элементы.

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

Таким образом, вам нужно реализовать как конструктор копирования, так и оператор присваивания для обеспечения правильной семантики копирования, например:

class Foo
{
private:
    int *dynamic_int;

public:
    Foo ()
    {
        dynamic_int = new int;
    }

    Foo (const Foo& foo)
    {
        dynamic_int = new int(*foo.dynamic_int);
    }

    Foo &operator=(const Foo& foo)
    {
        delete dynamic_int;
        dynamic_int = new int(*foo.dynamic_int);
    }

    ~Foo ()
    {
        delete dynamic_int;
    }
};

Правило для трех это хорошо иметь в виду и хорошо применяется здесь: у вас есть особое поведение уничтожения, так что, вероятно, вам также нужно настраивать поведение копирования.

0 голосов
/ 24 декабря 2011

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

assignment: i.e. operator =
copy constructor

Поскольку это очень распространенная проблема, другие люди придумали решения, а именно умные указатели.Это указатели, которые заботятся о копировании для вас.В библиотеке Boost они есть.Есть несколько на выбор, и каждый ведет себя по-своему.То, что вы хотите, зависит от того, чего вы хотите достичь.Вы хотите, чтобы копии указывали на один и тот же кусок памяти, или вы хотите, чтобы каждый указывал на свою выделенную память и т. Д.

...