Элемент класса необработанного указателя вложенного класса не может вернуть назначенные данные, указывающие на объект - PullRequest
1 голос
/ 04 октября 2019

У меня есть держатель класса H с unique_ptr для экземпляра класса A (H является владельцем A). Класс A определяет вложенный класс B и имеет unique_ptr для экземпляра B (A является владельцем B). В свою очередь, экземпляр A B должен обновить аспекты своего A, и поэтому B имеет необработанный указатель на A, которому он принадлежит.

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

Я могу заставить это работать, когда класс держателя отсутствует (то есть A не принадлежит никому, но все еще указан его экземпляром B).

Рассмотрим этот код:

#include <iostream>
#include <vector>
#include <memory>

// define class A
class A{



public:
  A(int x, int y);

  // define class B within A
  class B {
  public:
    B(A& a, int y);

  private:
    int b_y;
    A* b_a;

  public:
    void update_B(){
      std::cout << "Updating y of B belonging to A with x value " << b_a->a_x << std::endl;
      b_y += 1;
    }
  }; // end of class B

private:
  int a_x;
  std::unique_ptr<B> a_b;

public:
  void update_A(){
    std::cout << "Updating A...";
    a_x += 1;
    std::cout << " A's x is now " << a_x << std::endl;
    a_b->update_B();
  }
}; // end of class A


// define class H
class H{
public:
  H(int x, int y);
private:
  std::unique_ptr<A> h_a;

public:
  void update_h(){
    h_a->update_A();
  }
}; // end of class H

// define class A's constructor
A::A(int x, int y) {
  std::cout << "...creating instance of A... " << std::endl;
  a_x = x;
  a_b = std::make_unique<B> (B(*this, y)); // assign the unique ptr to B
  std::cout << "...A's x is set to " << x << std::endl;
}


// define class B's constructor
A::B::B(A& a, int y) {
  std::cout << ":- Creating instance of B within A..." << std::endl;
  b_a = &a; // assign B's pointer to the A instance that called it
  b_y = y;
  std::cout << ":- ...B's y is set to " << b_y << std::endl;
  std::cout << ":- ...B belongs to an A with value " << b_a->a_x << std::endl;
}


// define class H's constructor
H::H(int x, int y){
  std::cout << "Creating instance of H" << std::endl;
  h_a = std::make_unique<A> (A(x, y)); // assign the unique ptr to A
}


// main function
int main()
{
  // create instance of H
  H h(2, 10);
  // update the H function, which calls update_A and update_y
  h.update_h();
  return 0;
}

Ожидаемый результат:

Creating instance of H
...creating instance of A... 
:- Creating instance of B within A...
:- ...B's y is set to 10
:- ...B belongs to an A with value 2
...A's x is set to 2
Updating A... A's x is now 3
Updating y of B belonging to A with x value 3

, но фактический результат:

Creating instance of H
...creating instance of A... 
:- Creating instance of B within A...
:- ...B's y is set to 10
:- ...B belongs to an A with value 2
...A's x is set to 2
Updating A... A's x is now 3
Updating y of B belonging to A with x value <some massive number>

Кажетсячто B не отслеживает экземпляр A, на который он первоначально указывал.

Я могу заставить код работать, явно передав *this функции B update_B(), например update_B(A& a){ b_a = &a; }, но я не понимаю, почему вышеприведенное не работает.

Большое спасибо за понимание.

1 Ответ

1 голос
/ 04 октября 2019

Вас укусил неявный конструктор перемещения A. Проблема заключается в следующей строке:

h_a = std::make_unique<A> (A(x, y));

Это создает уникальный указатель на A из заданного аргумента (ов), A(x, y). Итак, вы создаете A из уже созданного экземпляра A. Этот второй экземпляр создается с первого хода и поэтому принимает первый экземпляр B, который теперь имеет неправильный указатель на собственный экземпляр.

Это можно увидеть, если вы измените свой код навыведите идентификационные данные A и B, когда вы с ними что-то делаете:

Creating instance of H
...creating instance of A (0x7ffecd5a6d40)... 
:- Creating instance of B (0x7ffecd5a6cf0) within A (0x7ffecd5a6d40)...
:- ...B's y is set to 10
:- ...B belongs to an A with value 2
...A's x is set to 2
Updating A (0x19852a0)... A's x is now 3
Updating y of B (0x1985280) belonging to A (0x7ffecd5a6d40) with x value -849711776

Если я изменю ваш код на:

h_a = std::make_unique<A>(x, y);

... тогда это сработаеткак и ожидалось:

Creating instance of H
...creating instance of A (0x877280)... 
:- Creating instance of B (0x7ffe479156c0) within A (0x877280)...
:- ...B's y is set to 10
:- ...B belongs to an A with value 2
...A's x is set to 2
Updating A (0x877280)... A's x is now 3
Updating y of B (0x8772a0) belonging to A (0x877280) with x value 3

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...