Конструктор копирования C ++ - ошибка сегментации глубоких копий указателей - PullRequest
0 голосов
/ 21 марта 2020

Я недавно начал изучать C ++ с помощью Advanced Computer Science Fundamentals MOO C. У нас есть вызов, который объявляется в скрытом файле .h (не может быть изменен), и нам нужно кодировать все функции-члены / конструкторы / деструкторы, инициализированные в файле .h.

Чтобы избежать мошенничества или того, чтобы кто-то делал за меня работу, я был бы признателен, если кто-нибудь может помочь объяснить, что я делаю неправильно и что я делаю, чтобы получить ошибку сегментации. Я искал и искал все глубокие и мелкие руководства по копированию, но не могу понять, что я делаю не так с этим.

Вот файл .h:

  class Pair {
    public:
      int *pa,*pb;
      Pair(int, int);
      Pair(const Pair &);
      ~Pair();
   };

Из этого следует отметить две вещи: pa / pb - это указатели на целые, а не на целые, и что здесь нет места для оператор присваивания, хотя я читал о «Правиле Большой Тройки», в котором объяснялось, если у меня есть конструктор копирования или деструкторы, у меня также должен быть оператор присваивания.

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

Пользовательский конструктор Я думаю, что все в порядке:

 Pair::Pair (int a,int b) {
      int *pa = new int(a);
      int *pb = new int(b);
      std::cout << "pa points to value : " << *pa <<std::endl;
      std::cout << "pb points to value : " << *pb <<std::endl;
      std::cout << "custom constructor resolved "<<std::endl;
    }

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

    Pair::Pair(const Pair &obj) {

    int *const * a = &obj.pa;
    int *const * b = &obj.pb;
    int *pa = new int;
    int *pb = new int;
    pa = *a;
    pb = *b;

    std::cout << "obj.pa address is : " << &obj.pa <<std::endl;
    std::cout << "obj.pb address is : " << &obj.pb <<std::endl;
    std::cout << "obj.pa points at : " << obj.pa <<std::endl;
    std::cout << "obj.pb points at : " << obj.pb <<std::endl;

    std::cout << "pa address is : " << &pa <<std::endl;
    std::cout << "pb address is : " << &pb <<std::endl;
    std::cout << "pa is pointing at : " << pa <<std::endl;
    std::cout << "pb is pointing at : " << pb <<std::endl;
    std::cout << "copy constructor called "<<std::endl;
     }

Деструктор Я думаю, я справился с этим:

    Pair::~Pair() {
      delete pa; pa = nullptr;
      std::cout << "pa deleted " << std::endl;

      delete pb; pb = nullptr;
      std::cout << "pb deleted " << std::endl;
    }

Main Серия тестов

int main() {
  Pair p(15,16);
  Pair q(p);
  Pair *hp = new Pair(23,42);
  delete hp;

  std::cout << "If this message is printed,"
    << " at least the program hasn't crashed yet!\n"
    << "But you may want to print other diagnostic messages too." << std::endl;
  return 0;
}

Глядя на это, программа создаст пару p; затем создайте пару глубоких копий q; затем создайте указатель на пару hp, который указывает на пару 23,42 в куче; а затем удаляет указатель на пару hp. Кажется, все компилируется и работает правильно, за исключением конструктора копирования. Основная проблема заключается в том, что & obj.pa, похоже, просто извлекает адрес, на который указывает указатель * pa объекта obj, а не извлекает фактическое разыменованное значение.

Для получения дополнительной информации здесь вывод терминала при выполнении .main (добавление дополнительного комментария):

pa points to value : 15            // pair p.pa correct
pb points to value : 16            // pair p.pb correct
custom constructor resolved        // pair p made
obj.pa address is : 0x7fff1887c780 // address original object pointer pa stored 
obj.pb address is : 0x7fff1887c788 // address original object pointer pb stored
obj.pa points at : 0x2             // address pointer obj pa is directed to (I want the value not this)
obj.pb points at : 0x40102d        // address pointer obj pb is directed to (I wand the value not this)
pa address is : 0x7fff1887c728     // pa address on stack
pb address is : 0x7fff1887c730     // pb address on stack
pa is pointing at : 0x2            // address value copied not address itself
pb is pointing at : 0x40102d       // address value copied not address itself
copy constructor called            // copy constructor runs through albeit incorrect
pa points to value : 23            // hp.pa
pb points to value : 42            // hp.pb
custom constructor resolved        // constructor made on heap from pointer hp
pa deleted deleted original pa     // (made from pointer to pair hp)
pb deleted deleted original pa     // (made from pointer to pair hp)
If this message is printed, at least the program hasn't crashed yet!
But you may want to print other diagnostic messages too.
pa deleted                         // deleted original pa (made from pair p)
pb deleted                         // deleted original pb (made from pair p)

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

1 Ответ

4 голосов
/ 21 марта 2020

У вас есть две существенные ошибки.

Сначала вы переделываете указатели внутри своих конструкторов.

Pair::Pair (int a,int b) {
    int *pa = new int(a);
    int *pb = new int(b);
    ...

должно быть

Pair::Pair (int a,int b) {
    pa = new int(a);
    pb = new int(b);
    ...

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

Точно такая же проблема в конструкторе копирования.

Во-вторых вы не копируете содержимое указателей с правой стороны, когда выполняете конструктор копирования. Вы копируете сами указатели.

На самом деле это намного проще, чем просто, просто адаптируйте код из вашего другого конструктора.

Pair::Pair(const Pair &obj) {
    pa = new int(*obj.pa);
    pb = new int(*obj.pb);

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

Или, если вы предпочитаете меньшие шаги

Pair::Pair(const Pair &obj) {
    int a = *obj.pa;
    int b = *obj.pb;
    pa = new int(a);
    pb = new int(b);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...